繁体   English   中英

在 C# 中使用“out”关键字的最佳实践

[英]Best practice of using the “out” keyword in C#

我正在尝试将 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”。

想法?

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

这是.NET Framework Developer's Guide关于 out 参数的说明:

避免使用 out 或 reference 参数。

使用定义输出或引用参数的成员要求开发人员了解指针、值类型和引用类型之间的细微差别以及输出和引用参数之间的初始化差异。

但是,如果您确实使用它们

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

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

你的方法总比外面好,因为你可以用这种方式“链接”调用:

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

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

用“out”实现的 TryParse 方法是一个错误,IMO。 这些在连锁店中会非常方便。

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

例如,如果您想获取文本字符串中最常见的单词和文本中的第 42 个单词,则可以使用相同的方法计算两者(只需解析文本一次)。 但是对于您的应用程序,这些信息彼此无关:出于统计目的,您需要最常用的词,但您只需要第 42 个词,因为您的客户是 Douglas Adams 的极客粉丝。

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

我只需要补充一点,从 C# 7 开始,当与内联变量声明结合使用时,在某些情况下使用out 关键字可以使代码变得非常可读。 虽然通常你应该返回一个(命名的)元组,但当方法具有布尔结果时,控制流变得非常简洁,例如:

if (int.TryParse(mightBeCount, out var count)
{
    // Successfully parsed count
}

我还应该提到,为那些元组有意义的情况定义一个特定的类,通常更合适。 这取决于有多少返回值以及您使用它们的目的。 我会说,当超过 3 个时,无论如何都要把它们放在一个班级里。

如果您甚至见过并使用过 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 要么懒惰地创建一个类型,要么想尝试一种语言功能。 即使是上面完全人为设计的MostCommonAnd42ndWord示例,我也会使用 List 或具有 2 个属性的新类型MostCommonAnd42ndWord

我在上面的解释中看到的唯一很好的原因是在被迫的互操作场景中。 假设这是有效的陈述。

远离out 它在那里是一种低级的便利。 但在高层次上,这是一种反技术。

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

out一个优点是编译器将验证CalcSomething确实为someOtherNumber分配了一个值。 它不会验证 Result 的 someOtherNumber 字段是否有值。

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

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

如果您的结果比单个值更复杂,您应该尽可能创建一个结果对象。 我要说这些的原因是什么?

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM