首页 文章

C#中引用类型变量的“ref”有什么用?

提问于
浏览
151

我理解,如果我传递一个值类型( intstruct 等)作为参数(没有 ref 关键字),该变量的副本将传递给该方法,但如果我使用 ref 关键字对该方法的引用传递变量,而不是新变量 .

但是使用引用类型(如类),即使没有 ref 关键字,也会将引用传递给方法,而不是副本 . 那么 ref 关键字与引用类型的用途是什么?


举个例子:

var x = new Foo();

以下有什么区别?

void Bar(Foo y) {
    y.Name = "2";
}

void Bar(ref Foo y) {
    y.Name = "2";
}

10 回答

  • 4

    它允许您修改传入的引用 . 例如

    void Bar()
    {
        var y = new Foo();
        Baz(ref y);
    }
    
    void Baz(ref Foo y)
    {
        y.Name = "2";
    
        // Overwrite the reference
        y = new Foo();
    }
    

    如果您不关心传入的引用,也可以使用 out

    void Bar()
    {
        var y = new Foo();
        Baz(out y);
    }
    
    void Baz(out Foo y)
    {
        // Return a new reference
        y = new Foo();
    }
    
  • 9

    在某些情况下,您要修改实际的 reference 而不是指向的对象:

    void Swap<T>(ref T x, ref T y) {
        T t = x;
        x = y;
        y = t;
    }
    
    var test = new[] { "0", "1" };
    Swap(ref test[0], ref test[1]);
    
  • 5

    Jon Skeet写了关于C#参数传递的a great article . 它详细说明了按值,引用( ref )和输出( out )传递参数的确切行为和用法 .

    以下是该页面与 ref 参数相关的重要引用:

    引用参数不传递函数成员调用中使用的变量的值 - 它们使用变量本身 . 不是在函数成员声明中为变量创建新的存储位置,而是使用相同的存储位置,因此函数成员中的变量值和引用参数的值将始终相同 . 引用参数需要ref修饰符作为声明和调用的一部分 - 这意味着当你通过引用传递某些东西时它总是很清楚 .

  • 1

    这里非常好解释:http://msdn.microsoft.com/en-us/library/s6938f28.aspx

    摘自文章:

    引用类型的变量不直接包含其数据;它包含对其数据的引用 . 按值传递reference-type参数时,可以更改引用指向的数据,例如类成员的值 . 但是,您无法更改引用本身的值;也就是说,您不能使用相同的引用为新类分配内存并使其在块外部保留 . 为此,请使用ref或out关键字传递参数 .

  • 29

    您可以使用 y 更改 foo 指向的内容:

    Foo foo = new Foo("1");
    
    void Bar(ref Foo y)
    {
        y = new Foo("2");
    }
    
    Bar(ref foo);
    // foo.Name == "2"
    
  • -1

    使用ref关键字传递引用类型时,通过引用传递引用,并且调用的方法可以为参数指定新值 . 该更改将传播到调用范围 . 如果没有ref,引用将按值传递,这不会发生 .

    C#也有'out'关键字,它与ref非常相似,除了使用'ref'之外,必须在调用方法之前初始化参数,并且使用'out'必须在接收方法中指定一个值 .

  • 3

    除了现有的答案:

    当您询问两种方法的区别时:使用 refout 时没有co(ntra)方差:

    class Foo { }
    class FooBar : Foo { }
    
    static void Bar(Foo foo) { }
    static void Bar(ref Foo foo) { foo = new Foo(); }
    
    void Main()
    {
        Foo foo = null;
        Bar(foo);           // OK
        Bar(ref foo);       // OK
    
        FooBar fooBar = null;
        Bar(fooBar);        // OK (covariance)
        Bar(ref fooBar);    // compile time error
    }
    
  • 17

    方法中的参数似乎总是传递副本,问题是什么的副本 . 副本由对象的复制构造函数完成,因为所有变量都是C#中的Object,我相信所有这些都是这种情况 . 变量(对象)就像生活在某些地址的人一样 . 我们要么改变居住在这些地址的人们,要么我们可以为电话簿中那些地址的人们创建更多的参考资料(制作浅层副本) . 因此,多个标识符可以引用相同的地址 . 引用类型需要更多空间,因此与通过箭头直接连接到堆栈中的标识符的值类型不同,它们具有堆中另一个地址的值(更大的空间) . 这个空间需要从堆中获取 .

    值类型:标识符(包含值=堆栈值的地址)---->值类型的值

    引用类型:标识符(包含值=堆栈值的地址)---->(包含值=堆值的地址)---->堆值(通常包含其他值的地址),想象更多箭头粘在不同的数组[0],数组[1],数组[2]的方向

    更改值的唯一方法是遵循箭头 . 如果一个箭头丢失/更改的方式无法访问该值 .

  • 139

    参考变量将地址从一个地方带到另一个地方,因此任何地方对它们的任何更新都将反映在所有地方,那么REF的用途是什么 . 引用变量是好的,直到没有新的内存分配给方法中传递的引用变量 . 一旦新内存分配,那么对此的值更改将不会反映在任何地方 . 对于这个参考来 . Ref是引用的引用,因此每当新内存分配它就会知道因为它指向该位置,因此值可以由everyOne共享 . 您可以看到图像更清晰 .

    Ref in Reference Variable

  • 15

    另一堆代码

    class O
    {
        public int prop = 0;
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            O o1 = new O();
            o1.prop = 1;
    
            O o2 = new O();
            o2.prop = 2;
    
            o1modifier(o1);
            o2modifier(ref o2);
    
            Console.WriteLine("1 : " + o1.prop.ToString());
            Console.WriteLine("2 : " + o2.prop.ToString());
            Console.ReadLine();
        }
    
        static void o1modifier(O o)
        {
            o = new O();
            o.prop = 3;
        }
    
        static void o2modifier(ref O o)
        {
            o = new O();
            o.prop = 4;
        }
    }
    

相关问题