何时传递ref关键字

我已经阅读了passng和不在参数中传递ref之间的区别,但是,我何时想要使用它们?

例如,我在方法中有一些逻辑可以重构为自己的方法。 Resharper 4.5将其中一个参数作为ref类型,但如果我手动进行重构,我认为我不会这样做。

显然我缺少一些理解。 也许当编码中的某些类型或某些场景错过ref关键字时会发生什么情况会有所帮助?

谢谢

让我把它分解为两个问题:

1)何时应该在编写方法时使用ref / outforms参数声明?

当您希望您的方法能够读取和写入从调用者传入的变量时,请使用ref / out,而不是仅仅读取

2)为什么“提取方法”重构会产生ref参数?

我不知道Resharper的细节,但我可以猜一猜。 考虑以下邪恶的可变值类型:

 struct S { private int x; public int X() { return this.x; } public void M() { this.x += 1; } } 

你有一个方法:

 void Foo() { S s = new S(); Fred(s); Blah(s); Bar(s); sM(); Console.WriteLine(sX()); // prints 1 } 

你在中间位做“提取方法”:

 void NewMethod(ref S s) { Blah(s); Bar(s); sM(); } void Foo() { S s = new S(); Fred(s); NewMethod(ref s); Console.WriteLine(sX()); // still prints 1 } 

相反,如果你创建了一个没有“ref”的方法,那么调用NewMethod会将s的副本传递给NewMethod。 请记住,值类型按值复制; 这就是为什么我们称它们为“价值类型”。 它将是变异的副本,然后sX()返回零。 重构在程序中引入语义变化是一个坏主意,重构引擎很难知道给定的方法是否依赖于值类型的可变性。

这只是你应该避免可变值类型的另一个原因。

传递byref仅对函数的“副作用”有意义:即,您打算修改值类型参数,或者将另一个对象重新分配给给定的对象参数,并使该更改在函数调用中继续存在。 示例: TryGetValue()

否则,最好坚持使用byval

从概念上讲,不同之处在于值类型直接存储其值,而引用类型存储对该值的引用。 也许你应该重读一下有关引用和值类型的内容。

通过引用传递值类型 – 如上所示 – 很有用,但ref对于传递引用类型也很有用。 这允许被调用的方法修改引用引用的对象,因为引用本身是通过引用传递的。 以下示例显示当引用类型作为ref参数传递时,可以更改对象本身。

  class RefRefExample { static void Method(ref string s) { s = "changed"; } static void Main() { string str = "original"; Method(ref str); // str is now "changed" } } 

MSDN

结果是,当控制传递回调用方法时,方法中参数的任何更改都将反映在该变量中。 因此,如果您希望分配给参数的值在方法调用之后继续存在,那么它就是一个可能的用例

考虑这个例子:

 static int i = 3; public static void ChangeIntRef(ref int val) { val = 5; } public static void ChangeInt(int val) { val = 5; } Console.WriteLine(i); ChangeInt(i); Console.WriteLine(i); ChangeIntRef(ref i); Console.WriteLine(i); 

通过将参数作为ref传递,您告诉编译器您实际需要的是对要传递给方法的原始变量的引用。 结果,该方法可以改变原始变量的值。

如果您从上面运行代码段,结果是:

 3 3 5 

这应该清楚地表明,如果没有ref关键字, ChangeInt方法将无法实际更改原始值。 但是,使用ref关键字, ChangeIntRef方法可以更改原始值。

我使用语义参考。 考虑这种方法:

 void AddResultsTable(ref PlaceHolder p) // modifies p; adding a table { var t = new Table(); AddTableHeader(ref t); // modifies t; adding a table header AddTableBody(ref t); // modifies t; adding a table body AddTableFooter(ref t); // modifies t; adding a table footer p.Controls.Add(t); } AddResultsTable(ref PlaceHolderResults); 

与这一个:

 Table ReturnTable() { var t new Table(); // AddTableHeader() returns TableHeader t.Columns.HeaderColumns.Add(ReturnTableHeader()); // ... etc. return t; } PlaceHolder.Controls.Add(ReturnTable()); 

第一段代码对我来说看起来更干净; 方法修改对象而不是返回您必须添加的新对象。 它们都在方法中保持“盒装”并隐藏起来。