为什么在传递对象时使用'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”,这意味着对象被作为参考传递。

通过一个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; } 

您需要区分“按值传递参考”和“按参考传递参数/参数”。

我已经写了一篇相当长的文章,以避免在每次新闻组出现时都要小心谨慎地写:)

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

由于TestRef是一个类(它是引用对象),所以你可以改变t中的内容,而不用把它作为参考。 但是,如果您将t作为参考,则TestRef可以更改原始t所指的内容。 即使它指向一个不同的对象。

ref你可以写:

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

方法完成后,t将被改变。

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

如果foo传递给一个没有使用ref的方法,该方法可能会改变Object#24601。 由于这种变化, foo.Length可能不再等于4.然而,该方法本身将无法改变foo ,这将继续保持“Object#24601”。

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

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

通过在引用类型中使用ref关键字,您可以有效地将引用传递给引用。 在很多方面,它和使用out关键字是一样的out但是有一点小小的区别,那就是不能保证方法实际上将任何东西都分配给了ref参数。

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

ref仅仅为两个范围模仿(或表现)为一个全局区域:

  • 呼叫者
  • 被叫方。

如果你传递一个价值,然而事情是不同的。 你可以强制一个值通过引用传递。 例如,这允许您将一个整数传递给一个方法,并让该方法代表您修改整数。

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

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

旁注:类名TestRef在这个上下文中是一个可怕的选择;)。