When should I use out parameters?
我不明白什么时候应该使用输出参数,如果我需要返回多个类型,我会亲自将结果包装为一个新类型,我发现使用它比使用out更容易。
我见过这样的方法,
1 | public void Do(int arg1, int arg2, out int result) |
有没有真正有意义的案例?
当你有一个
1 | public bool DoSomething(int arg1, out string result); |
在这种情况下,返回可以指示函数是否成功,结果是否存储在out参数中。诚然,这个例子是人为设计的,因为您可以设计一种方法,在这种方法中,函数只返回一个
缺点是必须声明局部变量才能使用它们:
1 2 3 | string result; if (DoSomething(5, out result)) UpdateWithResult(result); |
而不是:
1 | UpdateWithResult(DoSomething(5)); |
然而,这可能不是一个缺点,这取决于你想要的设计。对于datetime,提供了两种方法(parse和typarse)。
以及大多数事情都要看情况而定。让我们看看选项
- 您可以返回您想要的任何内容作为函数的返回值
- 如果要返回多个值,或者函数已经有返回值,则可以使用out参数或创建一个新的复合类型,将所有这些值作为属性公开。
对于台盼而言,使用out参数是有效的-您不必创建一个新类型,该类型的开销为16B(在32B机器上),或者在调用后让它们被垃圾收集,从而产生性能成本。例如,可以从一个循环内调用typarse——这里是out params规则。对于不会在循环中调用的函数(即性能不是主要问题),返回单个复合对象可能是"更干净"(对旁观者来说是主观的)。现在,使用匿名类型和动态类型,可能会变得更容易。
注:
我认为out对于需要返回布尔值和值的情况很有用,比如typarse,但是如果编译器允许这样的情况会更好:
1 | bool isValid = int.TryParse("100", out int result = 0); |
我知道答案迟了很多年。如果不希望方法实例化要返回的新对象,out(和ref)也非常有用。这在高性能系统中是非常相关的,在这些系统中,您希望为您的方法实现亚微秒的性能。从内存访问的角度来看,实例化相对昂贵。
当然,在您发布的示例中,当您的方法需要返回多个值时,可以使用out参数:
1 | public void Do(int arg1, int arg2, out int result) |
使用out参数没有多大意义,因为您只返回一个值,如果删除out参数并放入int返回值,则可以更好地使用该方法:
1 | public int Do(int arg1, int arg2) |
输出参数有一些好处:
- 每个out参数都必须在方法返回之前被明确地赋值,如果您错过了赋值,代码将不会编译。
总之,我基本上尝试在我的私有API中使用参数来避免创建单独的类型来包装多个返回值,在我的公共API中,我只在与台盼模式匹配的方法上使用它们。
如果您总是创建一个类型,那么您的应用程序可能会出现很多混乱。
如前所述,一个典型的用例是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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); } |
至于"为什么不返回一个可以为空的类型":自框架1.x以来就存在函数类型,而可以为空的类型带有2.0(因为它们需要泛型)。那么,为什么不必要地打破兼容性,或者开始在某些类型的台盼之间引入不一致性呢?您总是可以编写自己的扩展方法来复制已经存在的功能(请参阅EricLipperts关于一个无关主题的文章,其中包括做/不做事情背后的一些推理)。
另一个用例是,如果必须返回多个不相关的值,即使这样做会触发一个警报,即您的方法可能做得太多。另一方面,如果您的方法类似于昂贵的数据库或Web服务调用,并且您希望缓存结果,那么这样做可能是有意义的。当然,您可以创建一个类型,但同样,这意味着应用程序中还有一个类型。
我有时会使用out参数来提高可读性,当读取方法名时,它比方法的输出更重要,特别是对于除了返回结果之外还执行命令的方法。
1 2 3 4 5 | StatusInfo a, b, c; Initialize(out a); Validate(a, out b); Process(b, out c); |
VS
1 2 3 | StatusInfo a = Initialize(); StatusInfo b = Validate(a); StatusInfo c = Process(b); |
至少对我来说,我在扫描的时候非常强调每行的前几个字符。在确认声明了一些"statusInfo"变量之后,我可以很容易地分辨出第一个示例中发生了什么。在第二个示例中,我看到的第一件事是检索到一堆statusinfo。我必须再扫描一次,看看这些方法会产生什么样的效果。
我不能将空值传递给台盼函数的out参数,这确实让我恼火。
不过,在某些情况下,我更喜欢它返回带有两个数据段的新类型。尤其是当它们在大多数情况下都是无关的,或者只有一个零件在一瞬间后才需要进行一次操作时。当我确实需要保存一个函数的结果值时,我非常喜欢有一个out参数,而不是一些我必须处理的随机结果和值类。
是的,这是有道理的。以这个为例。
1 2 3 4 5 6 7 8 9 | String strNum ="-1"; Int32 outNum; if (Int32.TryParse(strNum, out outNum)) { // success } else { // fail } |
如果操作在具有返回值的正常函数中失败,您可以返回什么?您当然不能返回-1来表示一个失败,因为这样失败返回值和开始解析的实际值之间就没有区别了。这就是为什么我们返回一个布尔值来查看它是否成功,如果成功了,那么我们已经安全地分配了"返回"值。
为返回值而创建一个类型对我来说似乎没有什么痛苦:—)首先,我必须创建一个返回值的类型,然后在调用方法中,我将返回类型的值分配给需要它的实际变量。
输出参数与使用类似。