简体   繁体   English

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

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

I'm trying to formalise the usage of the "out" keyword in c# for a project I'm on, particularly with respect to any public methods.我正在尝试将 c# 中“out”关键字的使用形式化,用于我正在进行的项目,特别是对于任何公共方法。 I can't seem to find any best practices out there and would like to know what is good or bad.我似乎找不到任何最佳实践,想知道什么是好是坏。

Sometimes I'm seeing some methods signatures that look like this:有时我会看到一些类似这样的方法签名:

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

At this point, it's just a feeling, this doesn't sit well with me.在这一点上,这只是一种感觉,这对我来说不太合适。 For some reason, I'd prefer to see:出于某种原因,我更愿意看到:

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

where the result is a type that contains a decimal and the someOtherNumber.其中结果是包含小数和 someOtherNumber 的类型。 I think this makes it easier to read.我认为这使阅读更容易。 It allows Result to be extended or have properties added without breaking code.它允许在不破坏代码的情况下扩展 Result 或添加属性。 It also means that the caller of this method doesn't have to declare a locally scoped "someOtherNumber" before calling.这也意味着此方法的调用者不必在调用之前声明本地范围的“someOtherNumber”。 From usage expectations, not all callers are going to be interested in "someOtherNumber".从使用预期来看,并非所有调用者都会对“someOtherNumber”感兴趣。

As a contrast, the only instances that I can think of right now within the .Net framework where "out" parameters make sense are in methods like TryParse().相比之下,我现在能想到的在 .Net 框架中“out”参数有意义的唯一实例是在 TryParse() 之类的方法中。 These actually make the caller write simpler code, whereby the caller is primarily going to be interested in the out parameter.这些实际上使调用者编写更简单的代码,从而调用者主要对 out 参数感兴趣。

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

I'm thinking that "out" should only be used if the return type is bool and the expected usages are where the "out" parameters will always be of interest to the caller, by design.我认为只有在返回类型为 bool 并且预期的用法是调用者始终对“out”参数感兴趣时,才应使用“out”。

Thoughts?想法?

There is a reason that one of the static code analysis (=FxCop) rules points at you when you use out parameters.当您使用out参数时,静态代码分析 (=FxCop) 规则之一指向您是有原因的。 I'd say: only use out when really needed in interop type scenarios.我会说:只使用out真正需要,在互操作类型的场景。 In all other cases, simply do not use out .在所有其他情况下,根本不要使用out But perhaps that's just me?但也许这只是我?

This is what the .NET Framework Developer's Guide has to say about out parameters:这是.NET Framework Developer's Guide关于 out 参数的说明:

Avoid using out or reference parameters.避免使用 out 或 reference 参数。

Working with members that define out or reference parameters requires that the developer understand pointers, subtle differences between value types and reference types, and initialization differences between out and reference parameters.使用定义输出或引用参数的成员要求开发人员了解指针、值类型和引用类型之间的细微差别以及输出和引用参数之间的初始化差异。

But if you do use them :但是,如果您确实使用它们

Do place all out parameters after all of the pass-by-value and ref parameters (excluding parameter arrays), even if this results in an inconsistency in parameter ordering between overloads .务必将所有 out 参数放在所有 pass-by-value 和 ref 参数(不包括参数数组)之后,即使这会导致重载之间的参数顺序不一致

This convention makes the method signature easier to understand.这种约定使方法签名更容易理解。

Your approach is better than out, because you can "chain" calls that way:你的方法总比外面好,因为你可以用这种方式“链接”调用:

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

as opposed to

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

The TryParse methods implemented with "out" was a mistake, IMO.用“out”实现的 TryParse 方法是一个错误,IMO。 Those would have been very convenient in chains.这些在连锁店中会非常方便。

There are only very few cases where I would use out .只有极少数情况下我会使用out One of them is if your method returns two variables that from an OO point of view do not belong into an object together.其中之一是如果您的方法返回从面向对象的角度来看不属于一个对象的两个变量。

If for example, you want to get the most common word in a text string, and the 42nd word in the text, you could compute both in the same method (having to parse the text only once).例如,如果您想获取文本字符串中最常见的单词和文本中的第 42 个单词,则可以使用相同的方法计算两者(只需解析文本一次)。 But for your application, these informations have no relation to each other: You need the most common word for statistical purposes, but you only need the 42nd word because your customer is a geeky Douglas Adams fan.但是对于您的应用程序,这些信息彼此无关:出于统计目的,您需要最常用的词,但您只需要第 42 个词,因为您的客户是 Douglas Adams 的极客粉丝。

Yes, that example is very contrived, but I haven't got a better one...是的,那个例子非常人为,但我没有更好的例子......

I just had to add that starting from C# 7, the use of the out keyword makes for very readable code in certain instances, when combined with inline variable declaration.我只需要补充一点,从 C# 7 开始,当与内联变量声明结合使用时,在某些情况下使用out 关键字可以使代码变得非常可读。 While in general you should rather return a (named) tuple , control flow becomes very concise when a method has a boolean outcome, like:虽然通常你应该返回一个(命名的)元组,但当方法具有布尔结果时,控制流变得非常简洁,例如:

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

I should also mention, that defining a specific class for those cases where a tuple makes sense, more often than not, is more appropriate.我还应该提到,为那些元组有意义的情况定义一个特定的类,通常更合适。 It depends on how many return values there are and what you use them for.这取决于有多少返回值以及您使用它们的目的。 I'd say, when more than 3, stick them in a class anyway.我会说,当超过 3 个时,无论如何都要把它们放在一个班级里。

If you have even seen and worked with MS namespace System.Web.Security如果您甚至见过并使用过 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);

You will need a bucket.你需要一个水桶。 This is an example of a class breaking many design paradigms.这是一个打破许多设计范式的类的例子。 Awful!可怕!

Just because the language has out parameters doesn't mean they should be used.仅仅因为语言有参数并不意味着它们应该被使用。 eg goto例如转到

The use of out Looks more like the Dev was either Lazy to create a type or wanted to try a language feature.使用 out 看起来更像是 Dev 要么懒惰地创建一个类型,要么想尝试一种语言功能。 Even the completely contrived MostCommonAnd42ndWord example above I would use List or a new type contrivedresult with 2 properties.即使是上面完全人为设计的MostCommonAnd42ndWord示例,我也会使用 List 或具有 2 个属性的新类型MostCommonAnd42ndWord

The only good reasons i've seen in the explanations above was in interop scenarios when forced to.我在上面的解释中看到的唯一很好的原因是在被迫的互操作场景中。 Assuming that is valid statement.假设这是有效的陈述。

Stay away from out .远离out It's there as a low-level convenience.它在那里是一种低级的便利。 But at a high level, it's an anti-technique.但在高层次上,这是一种反技术。

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

One advantage of out is that the compiler will verify that CalcSomething does in fact assign a value to someOtherNumber . out一个优点是编译器将验证CalcSomething确实为someOtherNumber分配了一个值。 It will not verify that the someOtherNumber field of Result has a value.它不会验证 Result 的 someOtherNumber 字段是否有值。

You could create a generic tuple class for the purpose of returning multiple values.您可以创建一个通用元组类以返回多个值。 This seems to be a decent solution but I can't help but feel that you lose a bit of readability by returning such a generic type ( Result is no better in that regard).这似乎是一个不错的解决方案,但我不禁觉得通过返回这样的泛型类型会失去一些可读性(在这方面Result并没有更好)。

One important point, though, that james curran also pointed out, is that the compiler enforces an assignment of the value.但是,james curran 还指出的一个重要点是编译器强制执行值的分配。 This is a general pattern I see in C#, that you must state certain things explicitly, for more readable code.这是我在 C# 中看到的一般模式,您必须明确说明某些事情,以获得更具可读性的代码。 Another example of this is the override keyword which you don't have in Java.另一个例子是你在 Java 中没有的 override 关键字。

If your result is more complex than a single value, you should, if possible, create a result object.如果您的结果比单个值更复杂,您应该尽可能创建一个结果对象。 The reasons I have to say this?我要说这些的原因是什么?

  1. The entire result is encapsulated.整个结果被封装。 That is, you have a single package that informs the code of the complete result of CalcSomething.也就是说,您有一个包来通知 CalcSomething 的完整结果的代码。 Instead of having external code interpret what the decimal return value means, you can name the properties for your previous return value, Your someOtherNumber value, etc.您可以为之前的返回值、您的 someOtherNumber 值等命名属性,而不是让外部代码解释十进制返回值的含义。

  2. You can include more complex success indicators.您可以包含更复杂的成功指标。 The function call you wrote might throw an exception if end comes before start, but exception throwing is the only way to report errors.如果 end 在 start 之前,您编写的函数调用可能会抛出异常,但异常抛出是报告错误的唯一方法。 Using a result object, you can include a boolean or enumerated "Success" value, with appropriate error reporting.使用结果对象,您可以包含布尔值或枚举的“成功”值,并带有适当的错误报告。

  3. You can delay the execution of the result until you actually examine the "result" field.您可以延迟结果的执行,直到您实际检查“结果”字段。 That is, the execution of any computing needn't be done until you use the values.也就是说,在您使用这些值之前,不需要执行任何计算。

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

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