在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关键字。
如果结果比单个值更复杂,则应尽可能创建结果对象。 我不得不说这个的原因?
-
整个结果被封装。 也就是说,您有一个包可以通知代码CalcSomething的完整结果。 您可以为之前的返回值,someOtherNumber值等命名属性,而不是让外部代码解释十进制返回值的含义。
-
您可以包含更复杂的成功指标。 如果在开始之前结束,您编写的函数调用可能会抛出exception,但exception抛出是报告错误的唯一方法。 使用结果对象,您可以包含布尔值或枚举的“成功”值,并提供适当的错误报告。
-
您可以延迟执行结果,直到您实际检查“结果”字段。 也就是说,在使用这些值之前,不需要执行任何计算。