我已经阅读了passng与不在参数中传递ref之间的区别,但是,我何时想要使用它们?
例如,我在方法中有一些逻辑可以重构为自己的方法。 Resharper 4.5将其中一个参数作为ref类型,但如果我手动进行重构,我认为我不会这样做。
显然我缺少一些理解。也许当编码中的某些类型或某些场景错过ref关键字时会发生什么的一个例子会有用吗?
由于
答案 0 :(得分:8)
让我把它分解为两个问题:
1)何时应该在编写方法时使用ref / out形式参数声明?
当您希望您的方法能够读取和写入从调用者传入的变量而不是仅仅读取值时,请使用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);
s.M();
Console.WriteLine(s.X()); // prints 1
}
你在中间位上做“提取方法”:
void NewMethod(ref S s)
{
Blah(s);
Bar(s);
s.M();
}
void Foo()
{
S s = new S();
Fred(s);
NewMethod(ref s);
Console.WriteLine(s.X()); // still prints 1
}
如果你改为没有“ref”的方法,那么调用NewMethod会将s的副本传递给NewMethod。请记住,值类型按值复制;这就是为什么我们称它们为“价值类型”。它将是变异的副本,然后s.X()返回零。重构在程序中引入语义更改是一个坏主意,重构引擎很难知道给定方法是否依赖于值类型的可变性。
这只是你应该避免使用可变值类型的另一个原因。
答案 1 :(得分:1)
传递byref
仅对函数的“副作用”有意义:即,您打算修改值类型参数,或者将另一个对象重新分配给给定的对象参数,并使该更改在函数中存活呼叫。示例:TryGetValue()
。
否则,最好坚持byval
。
答案 2 :(得分:0)
从概念上讲,不同之处在于值类型直接存储其值,而引用类型存储对该值的引用。也许你应该重读一下有关参考值和值类型的信息。
通过引用传递值类型 - 如上所示 - 很有用,但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"
}
}
答案 3 :(得分:0)
效果是当控制传递回调用方法时,方法中参数的任何更改都将反映在该变量中。因此,如果您希望分配给参数的值在方法调用之后继续存在,那么它就是一个可能的用例
答案 4 :(得分:0)
考虑这个例子:
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
方法可以更改原始值。
答案 5 :(得分:0)
我使用语义参考。考虑这种方法:
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());
第一段代码对我来说看起来更干净;方法修改对象而不是返回您必须添加的新对象。在方法中,它都保持“盒装”并隐藏起来。