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

时间:2009-01-05 14:16:57

标签: c# .net

我正在尝试将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”参数感兴趣的地方。

思想?

10 个答案:

答案 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)

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

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

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

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