为什么在传递对象时使用'ref'关键字?

时间:2008-10-09 11:49:36

标签: c# .net pass-by-reference ref

如果我将对象传递给方法,为什么要使用ref关键字?这不是默认行为吗?

例如:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

输出为“Bar”,表示该对象作为参考传递。

11 个答案:

答案 0 :(得分:265)

如果您想更改对象的内容,请传递ref

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

在致电DoSomething后,t并未引用原始new TestRef,而是指一个完全不同的对象。

如果您想要更改不可变对象的值,例如,这也可能很有用。一个string。创建后,您无法更改string的值。但是通过使用ref,您可以创建一个函数来更改另一个具有不同值的字符串。

编辑:正如其他人提到的那样。除非需要,否则使用ref不是一个好主意。使用ref使方法可以自由地更改其他内容的参数,方法的调用者需要进行编码以确保它们能够处理这种可能性。

此外,当参数类型是对象时,对象变量始终充当对象的引用。这意味着当使用ref关键字时,您已获得对引用的引用。这允许您执行上面给出的示例中描述的操作。但是,当参数类型是原始值(例如int)时,如果在方法中分配了此参数,则在方法返回后将传入的参数值将更改:

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}

答案 1 :(得分:81)

您需要区分“按值传递引用”和“通过引用传递参数/参数”。

我写了一个reasonably long article on the subject,以避免每次出现在新闻组时都要小心写:)

答案 2 :(得分:43)

在.NET中,将任何参数传递给方法时,都会创建一个副本。在值类型中,表示对值进行的任何修改都在方法范围内,并在退出方法时丢失。

传递引用类型时,也会复制一个副本,但它是引用的副本,即现在内存中有两个对同一对象的引用。因此,如果使用引用来修改对象,则会对其进行修改。但是如果您修改引用本身 - 我们必须记住它是一个副本 - 那么退出该方法时也会丢失任何更改。

正如人们之前所说,作业是对参考的修改,因此丢失了:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

上述方法不会修改原始对象。

对您的示例进行一点修改

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }

答案 3 :(得分:17)

由于TestRef是一个类(它们是引用对象),因此您可以更改t中的内容而不将其作为ref传递。但是,如果将t作为ref传递,则TestRef可以更改原始t所指的内容。即使它指向不同的对象。

答案 4 :(得分:15)

使用ref,您可以写:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

方法完成后t将被更改。

答案 5 :(得分:7)

将引用类型的变量(例如foo)(例如List<T>)视为保存“Object#24601”形式的对象标识符。假设语句foo = new List<int> {1,5,7,9};导致foo持有“对象#24601”(包含四个项目的列表)。然后调用foo.Length将询问对象#24601的长度,它将响应4,因此foo.Length将等于4.

如果foo在未使用ref的情况下传递给方法,则该方法可能会对对象#24601进行更改。由于此类更改,foo.Length可能不再等于4.但是,方法本身将无法更改foo,这将继续保留“对象#24601”。

foo作为ref参数传递将允许被调用的方法不仅对对象#24601进行更改,还对foo本身进行更改。该方法可能会创建一个新的对象#8675309并在foo中存储对该对象的引用。如果是这样,foo将不再持有“对象#24601”,而是“对象#8675309”。

实际上,引用类型变量不包含“Object#8675309”形式的字符串;他们甚至没有任何可以有意义地转换成数字的东西。尽管每个引用类型变量将保持一些位模式,但存储在这些变量中的位模式与它们识别的对象之间没有固定的关系。代码无法从对象或对它的引用中提取信息,并且稍后确定另一个引用是否标识了相同的对象,除非代码持有或知道标识原始对象的引用。

答案 6 :(得分:5)

这就像将指针传递给C中的指针一样。在.NET中,这将允许您更改原始T引用的内容,个人虽然我认为如果您在.NET中这样做你可能有设计问题!

答案 7 :(得分:3)

通过将ref关键字与引用类型一起使用,您实际上是将引用传递给引用。在许多方面,它与使用out关键字相同,但略有不同,无法保证该方法实际上会为ref'ed参数指定任何内容。

答案 8 :(得分:3)

ref模仿(或行为)仅作为两个范围的全局区域:

  • 呼叫者
  • 被叫方。

答案 9 :(得分:1)

但是,如果你传递了一个值,那么事情会有所不同。您可以强制通过引用传递值。例如,这允许您将整数传递给方法,并让该方法代表您修改整数。

答案 10 :(得分:1)

Ref表示函数是否可以在对象本身上获取,或仅在其值上获取。

通过引用传递不受语言约束;它是一个参数绑定策略,旁边是按值传递,按名称传递,按需传递等等......

旁注:在此上下文中,类名TestRef是一个非常糟糕的选择;)。