为什么在C#中引用和退出?
使用关键字ref
,调用代码需要初始化传递的参数,但是使用关键字out
我们不需要这样做。
- 为什么我们
out
到处都用? - 这两者之间有什么区别?
- 请举例说明我们需要使用
ref
并且不能使用的out
?
答案在这篇MSDN文章中给出。 从那篇文章:
由
out
和ref
寻址的两个参数传递模式略有不同,但它们都非常常见。 这些模式之间的细微差别导致一些非常常见的编程错误。 这些包括:
- 不为所有控制流路径中的
out
参数赋值- 不将值赋给用作
ref
参数的变量因为C#语言为这些不同的参数传递模式分配了不同的明确赋值规则,所以这些常见的编码错误被编译器捕获为不正确的C#代码。
决定包括
ref
和out
参数传递模式的关键是允许编译器检测这些常见的编码错误值得在语言中同时具有ref
和out
参数传递模式的额外复杂性。
out
是一种特殊forms的ref
,其中引用的内存不应在调用之前初始化。
在这种情况下,C#编译器强制在方法返回之前分配out
变量,并且在分配之前不使用该变量。
两个例子,其中out
不起作用,但ref
确实:
void NoOp(out int value) // value must be assigned before method returns { } void Increment(out int value) // value cannot be used before it has been assigned { value = value + 1; }
这些答案都没有让我满意,所以这里是我对ref
的看法。
我的回答是以下两页的摘要:
- ref(C#参考)
- out(C#参考)
相比
- 方法定义和调用方法都必须明确使用
ref
/out
关键字 - 两个关键字都会导致参数通过引用传递( 偶数值类型 )
- 但是,通过引用传递值类型时没有装箱
- 属性不能通过
out
或ref
传递,因为属性确实是方法 -
ref
/out
在编译时不被认为是方法签名的一部分,因此如果它们之间的唯一区别是其中一个方法采用ref
参数而另一个方法采用out
参数,则方法不能重载
对比
ref
- 必须在传递之前进行初始化
- 可以用来将值传递给方法
out
- 在传递之前不必初始化
- 在方法返回之前,需要调用方法来赋值
- 不能用于将值传递给方法
例子
无法编译,因为只有方法签名的差异是ref
/ out
:
public void Add(out int i) { } public void Add(ref int i) { }
使用ref
关键字:
public void PrintNames(List names) { int index = 0; // initialize first (#1) foreach(string name in names) { IncrementIndex(ref index); Console.WriteLine(index.ToString() + ". " + name); } } public void IncrementIndex(ref int index) { index++; // initial value was passed in (#2) }
使用out
关键字:
public void PrintNames(List names) { foreach(string name in names) { int index; // not initialized (#1) GetIndex(out index); Console.WriteLine(index.ToString() + ". " + name); } } public void GetIndex(out int index) { index = IndexHelper.GetLatestIndex(); // needs to be assigned a value (#2 & #3) }
作者的随机评论
- 在我看来,使用
out
关键字的概念类似于使用ParameterDirection的Output
枚举值在ADO.NET中声明输出参数 - C#中的数组通过引用传递,但为了重新分配数组引用以影响调用代码中的
ref
必须使用ref
关键字
例:
public void ReassignArray(ref int[] array) { array = new int[10]; // now the array in the calling code // will point to this new object }
有关引用类型与值类型的更多信息,请参阅传递引用类型参数(C#编程指南)
一个人为的例子,当你需要使用ref而不是out时,如下:
public void SquareThisNumber(ref int number) { number = number * number; } int number = 4; SquareThisNumber(ref number);
这里我们希望number
是一个in-out变量,所以我们使用ref
。 如果我们用完out
,编译器会给出一个错误,说我们在使用之前初始化了一个out
参数。
ref关键字允许您更改参数的值。 被调用的方法可以是调用链中的中间链接。 使用out关键字的方法只能在调用链的开头使用。
另一个优点是现有值可以用在方法的逻辑中并且仍然保持返回值。
在Oracle函数中有显式的IN(默认和你没有设置方向时得到的)IN / OUT和OUT参数。 等效是正常的(只是参数),ref [参数]和out [参数]。
编译器知道在调用之前不应设置out变量。 这允许它们在使用前声明。 但是它知道它必须在返回时使用的函数之前设置。
当我们在调用以out
关键字为前缀的方法时传递值时,它会将它视为完全不同,就像我们没有将它传递给方法一样。 相反,我们实际上是从方法的定义部分收集(outing)out变量的值到我们调用该方法的method out变量参数。
因此out variable
是在方法定义中完成的处理输出,这就是我们需要创建它,初始化它并仅在定义中修改它的原因。
当我们需要从特定方法返回多个值时,使用out
变量。
在ref
变量的情况下,我们需要首先初始化它,因为它的内存位置被转换为方法定义作为参数。 想想如果我们在传递之前没有初始化会发生什么?