何时应该使用`out`参数而不是返回复杂类型?

6

何时应该在C#中使用out参数?

例如:

bool TryGetValue(out object value);

对比。

class ReturnType
{
      public bool Found {get;set;}
      public object Value {get;set;}
}

ReturnType TryGetValue();

除了减少代码行数之外,什么时候应该使用out参数,什么时候应该将其作为返回类型返回?
12个回答

4

Out还可用于可能会失败的操作(通常在以Try*开头的方法中找到)。

例如, TryParse将返回一个指示成功/失败的bool值,同时使用out值作为结果。这避免了抛出异常。


3

你已经准确地指出了为什么应该使用它。即为了减少代码复杂性。

if (TryGetValue(myObj))
{
    [...]
}

比另一种更整洁

Result tryGetValueResult = TryGetValue();
if (tryGetValueResult.Found)
{
    [...]
}

它还可以在结果需要是引用类型时节省垃圾。

这里有一个相关的线程:https://dev59.com/yHVC5IYBdhLWcg3w9GLM - razlebe
精简的代码、可扩展性和优雅之间存在微妙的平衡点。如果有两个以上的输出参数,最好将它们作为返回类型。 - Ramesh

3
如果没有一般性的原因将多个值封装在一起,除了为了这个调用之外,那么创建一个单独的类型可能过于复杂。
另一方面,如果您有多个输出参数和一个返回值,它们都在逻辑上属于同一个类别(例如名字、姓氏、电话号码),那么创建一个适当的类型(例如“联系人”)并返回该类型可能是有意义的。
对于TryGetValue选项的另一种选择是使用可空值类型,如果问题中的值是值类型。例如,int.TryParse可以具有以下签名:
int? TryParse(string text)

(当然还有一个采用IFormatProvider的重载)

3

它们在你需要从构造函数返回一个值的(罕见)情况下非常有用。例如,Mutex 构造函数具有一个布尔输出参数,用于指示是否创建了新的 Mutex,或者已经存在一个具有名称的 Mutex。


2
out关键字通过编译器强制执行几个事情...
调用方不必初始化参数。被调用方不能从参数中读取,但在退出方法之前必须写入它。

更重要的是,out使意图可见,因为在进行方法调用时,调用方必须指定out关键字。

当您需要从方法中返回多个值(而无需从object[]中填充和解压缩它们)时,可以使用out。(喜欢从每个函数返回bSuccess的部分将喜欢out关键字。)
在您发布的示例中... TryParse的主要返回值是解析操作的成功。因此,这是它的返回值-但最有可能的后续需求是如果成功,则获取已解析的对象。因此,TryParse通过将其作为out参数给出来节省了您的麻烦..因为它已经作为CanParse检查的一部分完成了该步骤。


1

最好使用像选项类型这样的容器来存储可能不存在的返回类型。这在大多数函数式语言(如Scala)中很普遍。对于只关心某些操作成功或失败的情况非常有用,而不关心原因。例如解析整数。

int num;  // mutable = bugprone
if (!int.TryParse(str, out num)) // branching = complexity
  num = someDefault;

使用Option类型,您可以定义一个整数解析器,它包装了可变的丑陋代码并返回一个干净的Option。这样可以通过仅在一个地方拥有此if语句来减少复杂性。避免重复和突变。

  public Option<int> IntParse(string str) {
    int num;  // mutable
    if (!int.TryParse(str, out num))
      return Option.None<int>();
    else
      return Option.Some<int>(num);
  }

在其他地方的使用:

int result = IntParse(someString).GetOrElse(defaultValue);

正如你所看到的,这会导致使用模式的复杂度显著降低,因为分支已被隐藏。

或者,如果您的函数必须返回两个值,则有可能:

  • 函数太复杂,应该拆分(做一件事情!),或者
  • 返回值密切相关,应该绑定在一个新类型中

需要考虑的问题!


1

输出参数通常比较脆弱。超越这个例子,从总体上看问题:Bill Wagner的书《More Effective C#》描述了一种相当聪明的方法,它在这里也有详细描述。

以下代码将在此情况下起作用。

public class Tuple<T1,T2>
{
  public T1 First {get;private set;}
  public T2 Second {get; private set;}
  Tuple(T1 first, T2 second)
  {
    First = first;
    Second = second;
  }
}


using ComplexPair = Tuple<bool,object>;

public class SomeOtherClass 
{
  public ComplexPair GetComplexType ()
  {
    ...
    return new ComplexPair(true, new object);

  }
}

0
在我看来,两种方法都是正确的,但是如果返回一个复杂类型,特别是泛型类型,会更加优雅。
ReturnType<T>

这提供了更多关于值会是什么的信息,因此您不需要猜测值的原始类型并进行强制转换。


0

如果对象可能为空,则无需实例化。


0

我会投票使用返回类型。即使C#不是一种函数式语言,我认为以函数式编程风格为目标是好的。这并不是为了学术纯粹性,而是为了实用的好处,其中每个函数的行为都是非神秘的,没有副作用。


1
副作用和神秘意味着函数会执行你没有预料到的操作。如果你将某些东西传递到一个输出位置,那么这不正是你所期望的吗? - Bill Wert - MSFT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接