什么时候应该使用参数?
我不明白什么时候应该使用输出参数,如果我需要返回多个types,我个人将结果换成新types,我发现使用输出参数比输出更容易。
我见过这样的方法,
public void Do(int arg1, int arg2, out int result)
有什么情况下,这实际上是有道理的?
怎么样TryParse
,为什么不返回一个ParseResult
types? 或者在较新的框架中返回一个无效types?
当你有一个TryNNN
函数时,输出是很好的,很明显,即使函数没有成功,输出参数也会一直被设置。 这允许您依赖于您声明的局部variables将被设置的事实,而不必在稍后的代码中将检查置于null。 (下面的注释表示参数可以设置为null
,所以您可能需要validation您调用的函数的文档,以确定是否是这种情况)。它使代码更清晰,更简单读书。 另一种情况是,当你需要返回一些数据和状态的方法如下:
public bool DoSomething(int arg1, out string result);
在这种情况下,返回值可以指示函数是否成功,结果存储在out参数中。 无可否认,这个例子是有意思的,因为你可以devise一个函数简单地返回一个string
,但是你明白了。
缺点是你必须声明一个局部variables来使用它们:
string result; if (DoSomething(5, out result)) UpdateWithResult(result);
代替:
UpdateWithResult(DoSomething(5));
然而,这可能不是一个劣势,这取决于你要去的devise。 在DateTime的情况下,提供了两个方法(Parse和TryParse)。
就像大多数事情一样。 让我们看看选项
- 你可以返回任何你想要的作为函数的返回值
- 如果你想返回多个值,或者函数已经有一个返回值,你可以使用params或创build一个新的复合types,公开所有这些值作为属性
在TryParse的情况下,使用out参数是有效的 – 你不必创build一个新的types,这将是16B的开销(在32b机器上),或者产生在调用后收集垃圾的性能。 可以从一个循环内调用TryParse – 所以在这里出来的params规则。
对于在循环中不被调用的函数(即,性能不是主要关心的问题),返回单个复合对象可能是“更干净”(对于旁观者而言是主观的)。 现在使用匿名types和dynamictypes,它可能变得更容易。
注意:
-
out
params有一些需要遵循的规则,即编译器将确保该函数在退出之前对值进行初始化。 因此,即使parsing操作失败,TryParse也必须将out参数设置为某个值 - TryXXX模式是何时使用params的一个很好的例子 – Int32.TryParse被引入,人们抱怨捕获exception的perf命中,以知道parsing失败。 另外,在parsing成功的情况下,最有可能的事情是获得parsing的值 – 使用out参数意味着您不必对Parse进行另一个方法调用
我认为out对于需要同时返回一个布尔值和一个值(比如TryParse)的情况很有用,但是如果编译器允许这样的话:
bool isValid = int.TryParse("100", out int result = 0);
当然,如果你有一个方法需要返回多个值,在你发布的例子中,out参数是打算使用的:
public void Do(int arg1, int arg2, out int result)
使用out参数没什么意义,因为只返回一个值,如果删除out参数并放置一个int返回值,该方法可以更好地使用:
public int Do(int arg1, int arg2)
关于out参数有一些好的东西:
- 输出参数最初被认为是未分配的。
- 在方法返回之前, 必须明确指定每个输出参数,如果错过任务,代码将不会编译。
总之,我基本上尝试在我的私人API中使用params来避免创build单独的types来包装多个返回值,在我的公共API上,我只在与TryParse模式匹配的方法上使用它们。
我知道,多年迟到了。 out(和ref)也是非常有用的,如果你不希望你的方法实例化一个新的对象返回。 这在高性能系统中非常重要,您希望为您的方法实现亚微秒性能。 从存储器访问angular度看,实例化相对昂贵。
为返回值创build一个types听起来有点痛苦:-)首先,我将不得不创build一个返回值的types,然后在调用方法中,我已经从返回的types的值分配给需要它的实际variables。
输出参数是simipler使用。
是的,这是有道理的。 以此为例。
String strNum = "-1"; Int32 outNum; if (Int32.TryParse(strNum, out outNum)) { // success } else { // fail }
如果在具有返回值的正常函数中操作失败,你会返回什么? 你当然不能返回-1来表示失败,因为那么失败返回值和被parsing的实际值之间没有区别。 这就是为什么我们返回一个布尔值,看看它是否成功,如果它确实,那么我们已经安全地分配了我们的“返回”值。
它让我烦恼,我不能将null传递给TryParse函数的out参数。
不过,在某些情况下,我更喜欢用两个数据返回一个新types。 特别是当他们大部分是不相关的,或者一时之后只需要一个单独的操作时就需要一个。 当我需要保存一个TryParse函数的结果值时,我真的很喜欢有一个out参数,而不是一些我必须处理的ResultAndValue类。
如果你总是创build一个types,那么你的应用程序可能会有很多混乱。
正如这里所说的,一个典型的用例是一个TrySomething
方法,其中你想返回一个bool作为成功的指标,然后是实际值。 我也发现在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); }
至于“为什么不返回可空types”:TryParse自Framework 1.x开始存在,而Nullable Types则为2.0(因为它们需要generics)。 那么为什么不必要地破坏兼容性,或者开始在某些types的TryParse之间引入不一致呢? 您可以随时编写自己的扩展名方法来复制已经存在的function(请参阅Eric Lipperts发布的一个无关的主题,其中包括做/不做的背后的一些推理)
另一个用例是,如果必须返回多个不相关的值,即使这样做会触发警报,说明您的方法可能做得太多。 另一方面,如果你的方法是昂贵的数据库或Web服务调用,并且你想caching结果,那么这样做可能是有意义的。 当然,你可以创build一个types,但是再一次,这意味着你的应用程序需要更多的types。
有时为了便于阅读,我使用了参数,当读取方法名称比方法的输出更重要时 – 特别是对于除了返回结果之外还执行命令的方法。
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);
至less对于我来说,当我扫描时,我会特别强调每行的前几个字符。 在确认已经声明了一些“StatusInfo”variables之后,我可以很容易地知道第一个例子中发生了什么。 在第二个例子中,我看到的第一件事就是检索了一堆StatusInfo。 我必须再次扫描以查看方法可能具有的效果。