56

我不明白什么时候应该使用输出参数,如果我需要返回多个类型,我个人将结果包装在一个新类型中,我发现使用起来比 out 容易得多。

我见过这样的方法,

   public void Do(int arg1, int arg2, out int result)

有没有真正有意义的情况?

怎么样TryParse,为什么不返回一个ParseResult类型?还是在较新的框架中返回一个可以为空的类型?

4

10 回答 10

31

当你有一个函数时 Out 很好TryNNN,很明显,即使函数不成功,out-parameter 也将始终设置。这允许您依赖于您声明的局部变量将被设置这一事实,而不必稍后在您的代码中对 null 进行检查。(下面的注释表明该参数可以设置为null,因此您可能需要验证您正在调用的函数的文档以确定是否是这种情况。)它使代码更清晰,更容易读。另一种情况是当您需要根据方法的条件返回一些数据和状态时,例如:

public bool DoSomething(int arg1, out string result);

在这种情况下,返回可以指示函数是否成功并且结果存储在 out 参数中。诚然,这个例子是人为的,因为你可以设计一种方法,让函数简单地返回 a string,但你明白了。

缺点是您必须声明一个局部变量才能使用它们:

string result;
if (DoSomething(5, out result))
    UpdateWithResult(result);

代替:

UpdateWithResult(DoSomething(5));

但是,这甚至可能不是缺点,这取决于您要进行的设计。在 DateTime 的情况下,提供了两种方法(Parse 和 TryParse)。

于 2009-07-23T05:43:00.613 回答
6

和大多数事情一样,这取决于。让我们看看选项

  • 你可以返回任何你想要的作为函数的返回值
  • 如果你想返回多个值或者函数已经有一个返回值,你可以使用 out params 或者创建一个新的复合类型,将所有这些值作为属性公开

在 TryParse 的情况下,使用 out 参数是有效的 - 您不必创建一个新的类型,该类型将是 16B 的开销(在 32b 机器上)或产生在调用后让它们被垃圾收集的性能成本。例如,可以从循环中调用 TryParse - 所以这里的参数规则。
对于不会在循环中调用的函数(即性能不是主要问题),返回单个复合对象可能更“干净”(对于旁观者来说是主观的)。现在有了匿名类型和动态类型,它可能会变得更加容易。

笔记:

  1. outparams 有一些需要遵循的规则,即编译器将确保函数在退出之前初始化值。因此,即使解析操作失败,TryParse 也必须将 out 参数设置为某个值
  2. TryXXX 模式是何时使用参数的一个很好的例子——引入了 Int32.TryParse,因为人们抱怨捕获异常以了解解析是否失败的性能命中。如果解析成功,您最有可能做的事情是获取解析值 - 使用输出参数意味着您不必对 Parse 进行另一个方法调用
于 2009-07-23T05:47:16.563 回答
6

我认为 out 对于需要返回布尔值和值的情况(例如 TryParse)很有用,但如果编译器允许这样的事情会很好:

bool isValid = int.TryParse("100", out int result = 0);
于 2009-07-23T05:53:44.460 回答
5

我知道,答案迟了几年。如果您不希望您的方法实例化要返回的新对象,out(以及 ref)也非常有用。这在您希望为您的方法实现亚微秒级性能的高性能系统中非常重要。从内存访问的角度来看,实例化相对昂贵。

于 2015-10-08T21:11:46.893 回答
3

当然,在您发布的示例中,当您有一个需要返回多个值的方法时,应使用 out 参数:

public void Do(int arg1, int arg2, out int result)

使用 out 参数没有多大意义,因为您只返回一个值,如果您删除 out 参数并放入 int 返回值,则可以更好地使用该方法:

public int Do(int arg1, int arg2)

输出参数有一些好处:

  1. 输出参数最初被认为是未分配的。
    • 每个 out 参数必须在方法返回之前明确分配,如果您错过分配,您的代码将无法编译。

总之,我基本上尝试在我的私有 API中使用 out params以避免创建单独的类型来包装多个返回值,并且在我的公共 API 上,我只在与 TryParse 模式匹配的方法上使用它们。

于 2009-07-23T06:04:09.027 回答
0

创建一个仅用于返回值的类型对我来说听起来并不痛苦:-) 首先,我必须创建一个用于返回值的类型,然后在调用方法中,我将返回类型中的值分配给需要它的实际变量。

输出参数更易于使用。

于 2009-07-23T05:40:29.200 回答
0

是的,这确实有道理。以此为例。

String strNum = "-1";
Int32 outNum;

if (Int32.TryParse(strNum, out outNum)) {
    // success
}
else {
    // fail
}

如果在具有返回值的普通函数中操作失败,您会返回什么?您当然不能返回 -1 来表示失败,因为这样失败返回值和开始解析的实际值之间就没有区别了。这就是为什么我们返回一个布尔值来查看它是否成功,如果成功了,那么我们已经安全地分配了我们的“返回”值。

于 2009-07-23T05:42:01.177 回答
0

我无法将 null 传递给 TryParse 函数的 out 参数,这让我很恼火。

不过,在某些情况下,我更喜欢它而不是返回带有两条数据的新类型。尤其是当它们大部分不相关或片刻后只需要单个操作时。当我确实需要保存 TryParse 函数的结果值时,我真的很喜欢有一个 out 参数,而不是我必须处理的一些随机 ResultAndValue 类。

于 2009-07-23T05:42:40.410 回答
0

如果您总是创建一个类型,那么您的应用程序中可能会出现很多混乱。

正如这里所说,一个典型的用例是一个TrySomething方法,您希望返回一个布尔值作为成功的指标,然后返回实际值。我还发现在 if 语句中更简洁一些——所有三个选项都大致具有相同的 LOC。

int myoutvalue;
if(int.TryParse("213",out myoutvalue){
    DoSomethingWith(myoutvalue);
}

vs.

ParseResult<int> myoutvalue = int.TryParse("213");
if ( myoutvalue.Success ) {
    DoSomethingWith(myoutvalue.Value);
}

vs.

int? myoutvalue = int.TryParse("213");
if(myoutvalue.HasValue){
    DoSomethingWith(myoutvalue.Value);
}

至于“为什么不返回 Nullable 类型”:TryParse 从 Framework 1.x 开始就存在,而 Nullable Types 是在 2.0 中出现的(因为它们需要泛型)。那么为什么不必要地破坏兼容性或开始在某些类型上引入 TryParse 之间的不一致呢?您始终可以编写自己的扩展方法来复制已经存在的功能(参见Eric Lipperts 发表的关于不相关主题的帖子,其中包括做/不做事背后的一些推理)

另一个用例是如果您必须返回多个不相关的值,即使您这样做应该会触发警报,表明您的方法可能做的太多。另一方面,如果您的方法类似于昂贵的数据库或 Web 服务调用,并且您想要缓存结果,那么这样做可能是有意义的。当然,您可以创建一个类型,但同样,这意味着您的应用程序中多了一个类型。

于 2009-07-23T06:01:08.533 回答
-1

我有时会使用out参数来提高可读性,因为读取方法名称比方法的输出更重要——特别是对于除了返回结果之外还执行命令的方法。

StatusInfo a, b, c;

Initialize(out a);
Validate(a, out b);
Process(b, out c);

对比

StatusInfo a = Initialize();
StatusInfo b = Validate(a);
StatusInfo c = Process(b);

至少对我来说,我在扫描时非常重视每行的前几个字符。在确认声明了一些“StatusInfo”变量后,我可以很容易地判断第一个示例中发生了什么。在第二个示例中,我看到的第一件事是检索到一堆 StatusInfo。我必须再次扫描,看看这些方法可能会产生什么样的效果。

于 2009-07-23T05:54:01.323 回答