我已经阅读了传递参数时使用和不使用 ref 的区别,但是什么情况下我会想要使用它们呢?
例如,我有一个方法中的一些逻辑可以重构成自己的方法。Resharper 4.5 将其中一个参数变为了 ref 类型,但如果我手动重构代码,我认为我不需要这样做。
显然,我还存在一些理解上的疑惑。也许举个例子,说明在某些类型或某些编码场景中缺少 ref 关键字会发生什么,会有所帮助吗?
谢谢
我已经阅读了传递参数时使用和不使用 ref 的区别,但是什么情况下我会想要使用它们呢?
例如,我有一个方法中的一些逻辑可以重构成自己的方法。Resharper 4.5 将其中一个参数变为了 ref 类型,但如果我手动重构代码,我认为我不需要这样做。
显然,我还存在一些理解上的疑惑。也许举个例子,说明在某些类型或某些编码场景中缺少 ref 关键字会发生什么,会有所帮助吗?
谢谢
让我将其分解成两个问题:
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
}
我使用引用来进行语义化。考虑一下这种方法:
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());
byref
只有在函数需要产生"副作用"时才有意义,即你打算修改值类型参数,或者将另一个对象重新分配给给定的对象参数,并且希望这个更改在函数调用后继续存在。例如:TryGetValue()
。
否则,最好坚持使用 byval
。
TryGetValue()
使用 out
关键字,调用方不需要初始化变量,但被调用方需要。ref
关键字用于被调用方可能更改对象引用的情况。 - Yannick Motton从概念上讲,值类型直接存储其值,而引用类型存储对该值的引用。也许你应该重新阅读一下关于引用类型和值类型的知识。
通过引用传递值类型--如上所示--是有用的,但 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"
}
}
考虑这个例子:
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
方法能够更改原始值。
这样做的效果是,当控制权返回到调用方法时,对参数所做的任何更改都将反映在该变量中。因此,如果您希望分配给参数的值在方法调用之后仍然存在,则可以考虑使用此功能。
out
参数? - Natrium