“out”参数在哪些情况下有用(无法使用“ref”替代)?

18
据我所知,out参数的唯一用途是允许调用方从单个方法调用中获取多个返回值。但我们也可以使用ref参数来获得多个结果值!那么是否存在其他情况下,out参数可能会证明其有用性并且无法使用ref参数替代呢?
谢谢。

注意:我刚刚在类似的问题上发布了这个答案,其中描述了一个场景,其中“out”参数对于从方法返回多个值以外的其他用途非常有用。 - stakx - no longer contributing
6个回答

29

是的——refout之间的区别在于明确赋值:

  • out参数在方法调用之前不需要由调用者进行明确的赋值。但是它必须在方法中在正常返回(即没有异常)之前进行明确的赋值。然后该变量在调用者中肯定被赋值了。

  • ref参数在方法调用之前必须由调用者进行明确的赋值。但它不需要在方法中分配不同的值。

因此,假设我们希望将int.TryParse(string, out int)更改为使用ref。通常调用代码如下:

int value;
if (int.TryParse(text, out value))
{
    // Use value
}
else
{
    // Do something else
}

现在,如果我们使用ref,我们需要在调用之前给value赋一个值,例如:

int value = 0;
if (int.TryParse(text, ref value))
{
    // Use value
}
else
{
    // Do something else
}

显然这不是一个非常巨大的差异 - 但会给人留下错误的印象。我们正在分配一个我们从来不打算使用的值,这对于可读性来说并不是一件好事。一个out参数表示一个值将会从方法中输出(假设没有异常),而你不需要有一个值作为输入。

我对C#5提出的一个建议(我不知道是否会被采纳),就是带有out参数的方法应该被视为返回一个值元组的方法。结合更好的元组支持,这意味着我们可以这样做:

var (ok, value) = int.TryParse(text);

在这种情况下,okvalue 将被隐式声明为 boolint。这样可以清楚地知道什么进入了方法(text),以及什么是返回值(两个信息:okvalue)。

如果 int.TryParse 使用 ref 参数,那么它将无法使用初始值,因为编译器无法知道是否真正关心 ref 参数的初始值。


+1 - 我喜欢通过out参数暗示元组结果值的想法。 - Thomas
1
@Thomas:这不是原创的 - 我从 F# 上抄袭了它 :) - Jon Skeet
1
(有点跑题)我同意Jon关于元组“解包”(你怎么称呼它)的请求。我仍然不明白为什么在.NET / C# 4.0中支持元组时会引起如此大的轰动,而且他们显然甚至不得不为此调整CLR,同时非常方便的功能却被省略了。这使得Tuple类型看起来非常琐碎,甚至几乎没有用处。 - stakx - no longer contributing
@stakx:我之前没有听说过元组支持需要CLR更改的事情。你有进一步信息的链接吗? - Jon Skeet
@Jon:我在PDC 2008会议记录(“Microsoft .NET Framework: CLR Futures”,在线@ http://channel9.msdn.com/pdc2008/PC49/)中听说了元组和CLR。我上面的评论是由于该讲话中一个不准确的幻灯片:虽然在时间索引00:13:15左右呈现的幻灯片表明*CLR*已更改以允许元组,但稍后的幻灯片(大约在时间索引00:16:06左右)表明元组包含在*BCL*中以实现互操作性(基本上是Jörg上面评论的内容)。这也更有意义。(在谈论CLR的演讲中听到BCL仍有点奇怪...) - stakx - no longer contributing
显示剩余2条评论

8
您可以这样看待函数参数:
普通参数为“in”参数:通过此类参数,一个值可以传递到函数中; 因此必须对其进行初始化。 “ref”参数为“in-out”参数:通过此类参数,一个值可以进出函数。由于前者,它也必须被初始化。 “out”参数为“out”参数:只有通过此类参数才能从函数中返回一个值; 因此,它不需要被初始化。
我是通过研究Microsoft的COM技术得出了这种看待“ref/out”参数的方式。IDL(接口描述语言)用于描述COM组件接口,在IDL中,参数会增加“in”、“out”和“inout”声明符。我怀疑.NET和C#在某种程度上从COM继承了这些声明符,尽管名称稍有不同(如“ref”代替“inout”)。
在COM中,“out”参数经常用于检索接口方法的实际返回值,因为“真正”的返回值通常已经用于返回一个HRESULT成功/错误代码。
在.NET中,“out”参数的重要性远不如在您想要从方法中返回多个值的情况下(在这些情况下,您可以返回复杂对象或元组)。

4

2
一个 out 参数在你想要从一个方法中获得多个结果值时非常有用。严格来说,你可以使用一个 ref 参数来实现同样的目标,但是 out 参数在传达意图方面做得更好。当你使用 ref 时,不清楚为什么要这样做,而不使用 out 或者使用函数结果。你可能打算改变传递的值,但是从函数签名上并不能清楚地看出为什么要改变它。

2
我认为一个很好的例子是int.TryParse()。

http://msdn.microsoft.com/en-us/library/f02979c7.aspx

“out”比“ref”更好的主要原因是,在调用之前(甚至是隐式地),你不需要为返回变量分配一个虚拟值。
因此,“out”告诉你和编译器:“这个变量将在方法中被赋值。如果有的话,变量的初始值甚至都不会被考虑。”

0
两者之间的主要区别在于,如果我们使用ref,那么我们必须在调用之前初始化它,并且在方法中为我们的ref变量分配一个值是可选的。
然而,对于out方法,我们不需要显式地初始化它们,但是在方法中我们必须为其赋值,否则会产生编译时错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接