在C#中使用“out”关键字的最佳做法

我正在尝试将c#中“out”关键字的使用forms化为我正在使用的项目,特别是对于任何公共方法。 我似乎无法找到任何最佳实践,并想知道什么是好或坏。

有时我看到一些方法签名看起来像这样:

public decimal CalcSomething(Date start, Date end, out int someOtherNumber){} 

在这一点上,这只是一种感觉,这并不适合我。 出于某种原因,我更愿意看到:

 public Result CalcSomething(Date start, Date end){} 

其中结果是包含小数和someOtherNumber的类型。 我认为这使得阅读更容易。 它允许扩展Result或添加属性而不会破坏代码。 这也意味着此方法的调用者不必在调用之前声明本地作用域“someOtherNumber”。 根据使用期望,并非所有呼叫者都会对“someOtherNumber”感兴趣。

相比之下,我现在可以在.Net框架中考虑“out”参数有意义的唯一实例是TryParse()等方法。 这些实际上使调用者编写更简单的代码,因此调用者主要对out参数感兴趣。

 int i; if(int.TryParse("1", i)){ DoSomething(i); } 

我认为只有在返回类型为bool时才会使用“out”,并且预期的用法是设计时调用者始终对“out”参数感兴趣的地方。

思考?

当您使用参数时,有一个原因是静态代码分析(= FxCop)规则指向您。 我会说:只有在互操作类型场景中真正需要时才使用。 在所有其他情况下,只是不要out 。 但也许那只是我?

这是.NET Framework Developer’s Guide关于参数的说法:

避免使用输出或参考参数。

使用定义或引用参数的成员需要开发人员理解指针,值类型和引用类型之间的细微差别,以及out和reference参数之间的初始化差异。

但是如果你确实使用它们 :

在所有pass-by-value和ref参数(不包括参数数组)之后放置所有参数,即使这会导致重载之间的参数排序不一致

此约定使方法签名更容易理解。

你的方法比出更好,因为你可以通过这种方式“链接”调用:

 DoSomethingElse(DoThing(a,b).Result); 

而不是

 DoThing(a, out b); DoSomethingElse(b); 

用“out”实现的TryParse方法是一个错误,IMO。 这些在链条上会非常方便。

只有极少数情况下我会out 。 其中之一是,如果您的方法返回两个变量,从OO的角度来看,它们不属于一个对象。

例如,如果要获取文本字符串中最常用的单词以及文本中的第42个单词,则可以使用相同的方法计算两者(必须仅解析文本一次)。 但是对于你的应用程序,这些信息彼此没有关系:你需要最常用的单词用于统计目的,但你只需要第42个单词,因为你的客户是一个令人讨厌的道格拉斯亚当斯粉丝。

是的,这个例子非常人为,但我没有更好的…

out一个优点是编译器将validationCalcSomething实际上是否为someOtherNumber分配值。 它不会validationResult的someOtherNumber字段是否具有值。

远离out 。 这是一个低级别的便利。 但从高层次来看,这是一种反技术。

 int? i = Util.TryParseInt32("1"); if(i == null) return; DoSomething(i); 

如果您甚至已经看过并使用过MS命名空间System.Web.Security

 MembershipProvider public abstract MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); 

你需要一个水桶。 这是一个打破许多设计范例的类的例子。 可怕!

仅仅因为语言有参数并不意味着它们应该被使用。 例如转到

使用out看起来更像Dev,要么是Lazy要么创建一个类型,要么想尝试语言function。 即使是上面完全做作的MostCommonAnd42ndWord示例,我也会使用List或具有2个属性的新类型MostCommonAnd42ndWord

我在上面的解释中看到的唯一好的理由是在被迫的互操作场景中。 假设这是有效的声明。

您可以创建一个通用元组类,以便返回多个值。 这似乎是一个不错的解决方案,但我不禁觉得你通过返回这样一个generics类型会失去一点可读性(在这方面Result并不好)。

但是,james curran也指出,重要的一点是编译器强制执行值的赋值。 这是我在C#中看到的一般模式,您必须明确说明某些事情,以获得更易读的代码。 另一个例子是您在Java中没有的override关键字。

如果结果比单个值更复杂,则应尽可能创建结果对象。 我不得不说这个的原因?

  1. 整个结果被封装。 也就是说,您有一个包可以通知代码CalcSomething的完整结果。 您可以为之前的返回值,someOtherNumber值等命名属性,而不是让外部代码解释十进制返回值的含义。

  2. 您可以包含更复杂的成功指标。 如果在开始之前结束,您编写的函数调用可能会抛出exception,但exception抛出是报告错误的唯一方法。 使用结果对象,您可以包含布尔值或枚举的“成功”值,并提供适当的错误报告。

  3. 您可以延迟执行结果,直到您实际检查“结果”字段。 也就是说,在使用这些值之前,不需要执行任何计算。