何时应该在C#中使用out
参数?
例如:
bool TryGetValue(out object value);
对比。
class ReturnType
{
public bool Found {get;set;}
public object Value {get;set;}
}
ReturnType TryGetValue();
除了减少代码行数之外,什么时候应该使用
out
参数,什么时候应该将其作为返回类型返回?Out还可用于可能会失败的操作(通常在以Try*开头的方法中找到)。
例如, TryParse将返回一个指示成功/失败的bool值,同时使用out值作为结果。这避免了抛出异常。
你已经准确地指出了为什么应该使用它。即为了减少代码复杂性。
if (TryGetValue(myObj))
{
[...]
}
比另一种更整洁
Result tryGetValueResult = TryGetValue();
if (tryGetValueResult.Found)
{
[...]
}
int? TryParse(string text)
IFormatProvider
的重载)它们在你需要从构造函数返回一个值的(罕见)情况下非常有用。例如,Mutex 构造函数具有一个布尔输出参数,用于指示是否创建了新的 Mutex,或者已经存在一个具有名称的 Mutex。
更重要的是,out使意图可见,因为在进行方法调用时,调用方必须指定out关键字。
当您需要从方法中返回多个值(而无需从object[]中填充和解压缩它们)时,可以使用out。(喜欢从每个函数返回bSuccess的部分将喜欢out关键字。)
在您发布的示例中... TryParse的主要返回值是解析操作的成功。因此,这是它的返回值-但最有可能的后续需求是如果成功,则获取已解析的对象。因此,TryParse通过将其作为out参数给出来节省了您的麻烦..因为它已经作为CanParse检查的一部分完成了该步骤。
最好使用像选项类型这样的容器来存储可能不存在的返回类型。这在大多数函数式语言(如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);
正如你所看到的,这会导致使用模式的复杂度显著降低,因为分支已被隐藏。
或者,如果您的函数必须返回两个值,则有可能:
需要考虑的问题!
输出参数通常比较脆弱。超越这个例子,从总体上看问题: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);
}
}
ReturnType<T>
这提供了更多关于值会是什么的信息,因此您不需要猜测值的原始类型并进行强制转换。
如果对象可能为空,则无需实例化。
我会投票使用返回类型。即使C#不是一种函数式语言,我认为以函数式编程风格为目标是好的。这并不是为了学术纯粹性,而是为了实用的好处,其中每个函数的行为都是非神秘的,没有副作用。