【问题标题】:What is the use of "ref" for reference-type variables in C#?C# 中引用类型变量的“ref”有什么用?
【发布时间】:2010-10-31 23:52:45
【问题描述】:

我了解,如果我将值类型(intstruct 等)作为参数传递(不带 ref 关键字),则会将该变量的副本传递给方法,但如果我使用 ref 关键字传递了对该变量的引用,而不是新变量。

但是对于像类这样的引用类型,即使没有ref 关键字,引用也会传递给方法,而不是副本。那么ref 关键字和引用类型有什么用呢?


举个例子:

var x = new Foo();

以下有什么区别?

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

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

【问题讨论】:

    标签: c# parameters pass-by-reference pass-by-value


    【解决方案1】:

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

    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]);
    

    【讨论】:

    • 这个答案给出了我需要更改原始引用而不是设置前一个实例的属性值的情况。
    【解决方案2】:

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

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

    【讨论】:

    • 所以你基本上得到了对原始参考的参考
    • 您可以更改原始参考“所指”的内容,所以可以。
    • 克里斯,你的解释很好;感谢您帮助我理解这个概念。
    • 那么在对象上使用'ref'就像在C++中使用双指针?
    • @TomHazel:-ish,前提是您在 C++ 中使用“双”指针来更改指针指向的内容。
    【解决方案3】:

    它允许你修改传入的引用。例如

    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();
    }
    

    【讨论】:

      【解决方案4】:

      当你用 ref 关键字传递一个引用类型时,你通过引用传递引用,你调用的方法可以给参数赋值一个新的值。该更改将传播到调用范围。如果没有 ref,则引用是按值传递的,这不会发生。

      C#也有'out'关键字,它很像ref,除了'ref',参数必须在调用方法之前初始化,而'out'你必须在接收方法中赋值。

      【讨论】:

        【解决方案5】:

        又一堆代码

        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;
            }
        }
        

        【讨论】:

          【解决方案6】:

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

          这是该页面中与ref 参数相关的重要引述:

          引用参数不传递 中使用的变量的值 函数成员调用 - 他们使用 变量本身。而不是 创建一个新的存储位置 函数成员中的变量 声明,相同的存储位置 被使用,所以变量的值 在函数成员和值中 参考参数的 是相同的。参考参数需要 ref 修饰符作为两者的一部分 声明和调用 - 那 意味着当你在 通过引用传递一些东西。

          【讨论】:

          • 我喜欢将您的狗皮带传递给朋友以按值传递引用的类比......但它很快就会崩溃,因为我认为您可能会注意到你的朋友在把皮带还给你之前把你的狗卖给了一只杜宾犬;-)
          【解决方案7】:

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

          文章摘要:

          引用类型的变量不直接包含它的数据;它 包含对其数据的引用。当您传递引用类型时 通过值参数,可以更改指向的数据 引用,例如类成员的值。然而,你 不能改变引用本身的值;也就是说,你不能 使用相同的引用为新类分配内存并拥有它 坚持在块外。为此,请使用 ref 或 out 关键字。

          【讨论】:

          • 解说确实很好。但是,不鼓励在 SO 上使用仅链接的答案。为了方便读者,我在这里添加了一篇文章的摘要。
          【解决方案8】:

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

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

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

          更改值的唯一方法是按照箭头。如果一个箭头丢失/改变了该值的方式,则无法访问。

          【讨论】:

            【解决方案9】:

            除了现有的答案:

            正如您询问两种方法的区别:使用refout 时没有协(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
            }
            

            【讨论】:

              【解决方案10】:

              引用变量将地址从一个地方带到另一个地方,因此在任何地方对它们的任何更新都会反映在所有地方 那么REF的用途是什么。 引用变量 (405) 一直有效,直到没有为方法中传递的引用变量分配新的内存。

              一旦分配了新的内存(410),那么这个对象(408)上的值变化将不会反映在任何地方。 因为这个 ref 来了。 Ref 是引用的引用,因此每当分配新内存时,它都会知道,因为它指向该位置,因此每个人都可以共享该值。您可以查看图像以获得更清晰的效果。

              【讨论】:

                猜你喜欢
                • 2018-06-26
                • 2013-11-29
                • 2011-02-15
                • 2017-09-21
                • 1970-01-01
                • 1970-01-01
                • 2011-06-30
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多