我正在尝试将c#中“out”关键字的使用形式化为我正在使用的项目,特别是对于任何公共方法。我似乎无法找到任何最佳实践,并想知道什么是好的或坏的。
有时我会看到一些看起来像这样的方法签名:
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”参数感兴趣的地方。
思想?
答案 0 :(得分:20)
当您使用out
参数时,有一个原因是静态代码分析(= FxCop)规则指向您。我会说:只有在互操作类型场景中真正需要时才使用out
。在所有其他情况下,只需不要使用out
。但也许那只是我?
答案 1 :(得分:15)
这是.NET Framework Developer's Guide关于参数的说法:
避免使用或引用参数。
与会员合作 定义或引用 参数需要开发人员 理解指针,微妙 价值类型和价值之间的差异 引用类型和初始化 out和reference之间的差异 参数。
但如果您使用them:
在所有pass-by-value和ref之后放置所有参数 参数(不包括参数 数组),即使这导致了 参数排序不一致 重载之间。
该惯例制定了该方法 签名更容易理解。
答案 2 :(得分:6)
你的方法比出更好,因为你可以用这种方式“链接”电话:
DoSomethingElse(DoThing(a,b).Result);
而不是
DoThing(a, out b);
DoSomethingElse(b);
用“out”实现的TryParse方法是一个错误,IMO。这些在链条上会非常方便。
答案 3 :(得分:4)
我使用out
的情况很少。其中之一是,如果您的方法返回两个从OO角度来看不属于对象的变量。
例如,如果要获取文本字符串中最常见的单词以及文本中的第42个单词,则可以使用相同的方法计算两者(必须仅解析文本一次)。但是对于你的应用程序,这些信息彼此没有关系:你需要最常用的单词用于统计目的,但你只需要第42个单词,因为你的客户是一个令人讨厌的道格拉斯亚当斯粉丝。
是的,那个例子非常做作,但我没有更好的...
答案 4 :(得分:1)
远离out
。这是一个低级别的便利。但从高层次来看,这是一种反技术。
int? i = Util.TryParseInt32("1");
if(i == null)
return;
DoSomething(i);
答案 5 :(得分:1)
out
的一个优点是编译器将验证CalcSomething
实际上是否为someOtherNumber
分配了值。它不会验证Result的someOtherNumber字段是否有值。
答案 6 :(得分:1)
如果你甚至见过并与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);
你需要一个水桶。这是一个打破许多设计范例的类的例子。可怕!
仅仅因为语言没有参数并不意味着它们应该被使用。例如goto
使用out看起来更像Dev,要么是Lazy要么创建一个类型,要么想尝试语言功能。
即使是我上面使用的完全做作的MostCommonAnd42ndWord
例子
列表或具有2个属性的新类型contrivedresult。
我在上面的解释中看到的唯一好的理由是在被迫的互操作场景中。假设这是有效的陈述。
答案 7 :(得分:1)
我只需要补充一点,从C#7开始,在与内联变量声明结合使用的情况下,out keyword的使用在某些情况下使代码易于阅读。通常,您应该返回一个(named) tuple,但是当方法具有布尔结果时,控制流会变得非常简洁,例如:
if (int.TryParse(mightBeCount, out var count)
{
// Successfully parsed count
}
我还应该提到,为那些通常使用元组有意义的情况定义一个特定的类更为合适。这取决于有多少个返回值以及将它们用于什么目的。我想说的是,当超过3个时,无论如何都要将它们放在一个班级中。
答案 8 :(得分:0)
您可以创建一个通用元组类,以便返回多个值。这似乎是一个不错的解决方案,但我不禁觉得你通过返回这样的泛型类型会失去一点可读性(Result
在这方面并不是更好。)
然而,james curran也指出,重要的一点是编译器强制执行值的赋值。这是我在C#中看到的一般模式,您必须明确说明某些事情,以获得更易读的代码。另一个例子是您在Java中没有的override关键字。
答案 9 :(得分:0)
如果结果比单个值更复杂,则应尽可能创建结果对象。我不得不这么说的原因?
整个结果已封装。也就是说,您有一个包可以通知代码CalcSomething的完整结果。您可以为之前的返回值,someOtherNumber值等命名属性,而不是让外部代码解释十进制返回值的含义。
您可以添加更复杂的成功指标。如果在开始之前结束,您编写的函数调用可能会抛出异常,但异常抛出是报告错误的唯一方法。使用结果对象,您可以包含布尔值或枚举的“成功”值,并提供适当的错误报告。
您可以延迟执行结果,直到您实际检查“结果”字段。也就是说,在使用这些值之前,不需要执行任何计算。