从一个方法中返回多个值

7
我可以想出以下方法来从一个方法中返回多个值(还有一种方法是将其拆分为两个方法)
private bool IsCarFaulty(Car car, out string fault)
{
  fault = string.Empty;
  return false;
}

private Tuple<bool, string> IsCarFaulty(Car car)
{
  return Tuple.Create(false, string.Empty);
}

private ResultAndMessage IsCarFaulty(Car car)
{
  return new ResultAndMessage(false, string.Empty);
}

private bool IsCarFaulty(Car car)
{
  return false;
}

private string GetCarFault(Car car)
{
  return string.Empty;
}

基本上我的问题是,是否存在一种情况,其中一种方法优于另一种方法?以 int.TryParse 为例。它使用了一个输出参数,但如果将其拆分成两个方法 CanParseParse ,是否同样可以发挥作用,甚至更好。

3
CanParse 和 Parse 会对整数进行两次解析。 - Tim Schmelter
6个回答

4
使用独立的CanParseParse方法的问题在于,您需要付出两次解析的代价:一次在CanParse中,然后在Parse中再次解析。当解析非常复杂且耗时(例如对于DateTime类型)时,这可能特别棘手。

你只回答了问题的一部分 ;) - Thomas Levesque

3
当需要返回多个值时,我更喜欢创建一个强类型的“Result”对象来封装方法的返回值。但是,需要返回多个值可能意味着您的方法正在做太多的事情,并且可以进行重构。
例如,您的方法返回一个布尔值表示汽车是否有故障,以及一条消息。为什么不让该方法返回一个枚举值表示故障类型,然后再编写一个负责将该值转换为错误消息的方法呢?

3
基本上我的问题是,有没有情况下一个更好?当然,我不认为有适用于所有情况的通用规则。只需根据具体情况选择您感觉更舒适的一种方法。通常我避免使用out参数,因为它们与Linq不兼容。如果以int.TryParse为例,它使用了一个out参数,但将其拆分为CanParse和Parse两个方法是否同样有效,甚至更好呢?当然,这可能有效,但这意味着字符串将被解析两次,这是次优的。当int.TryParse被引入时,C#没有可空类型;现在可以编写这样的方法:
public static int? TryParseInt32(string s)
{
    int i;
    if (int.TryParse(s, out i))
        return i;
    return null;
}

2
可空类型和int.TryParse都是在.NET2.0中引入的。 - Jon Hanna
@JonHanna,感谢您指出这一点,我以为TryParse从一开始就存在...在这种情况下,他们没有提供一个接受字符串并返回Nullable<T>TryParse重载似乎相当愚蠢;) - Thomas Levesque
是的,这似乎很自然。它可能早期就在beta版中完成了,由不同的团队完成,以匹配其他某个库(考虑Windows API有数百种情况,其中返回表示成功并且所需值通过指针写入 - 这是最接近的.NET形式),当他们不确定可空性是否会被删除时做出决定,或者另一个内部原因使其完全合理。或者他们可能喜欢将成功指示器与值分开。 - Jon Hanna
或者他们可能希望该模式匹配可空类型的情况,但在这种情况下,您无法使用它,因为无法区分失败和成功获取 null 的情况。 - Jon Hanna

2

选项:

  1. out
    • 快速,这就是为什么它在框架库中使用的原因,你不能用其他任何东西来交换速度。
    • 设计差,与LINQ和任何链接代码都不匹配。
    • 易于编码
  2. Tuple<>
    • ,创建小对象,但实际上很少会非常昂贵,因为据说在.NET中进行了优化。但我发现在几个场合下确实存在问题。
    • 可维护性差,没有字段名称,代码不是自描述的
    • 易于编码
  3. Result
    • 良好的可维护性
    • 更多的代码

显然,没有人能说哪种方法对你的特定情况更好,所以请自行选择。


1

返回多个值的最佳方法是返回一个对象(一个将所有值包装在属性中的类)

private ResultAndMessage IsCarFaulty(Car car)
{
  return new ResultAndMessage(false, string.Empty);
}

具有两个属性(布尔值和字符串)的ResultAndMessage类


0

我更喜欢在像TryParseTryDeque等方法中使用带有outbool返回值,原因如下:

  1. 结果的“部分”是非常独立的事物,我喜欢它们保持独立。
  2. 返回类型在所有变体中都是相同的。所以这是一致的。
  3. out类型总是与调用的类型或对象密切相关,要么是类型本身(如int.TryParse),要么是类型的类型参数(如Dictionary<TKey, TValue>.TryGetConcurrentQueue<T>.TryDequeue)。所以这是一致的。
  4. 它已经成为.NET中一个容易识别的模式。所以这是一致的。

最后一点的缺点是,当其他方法更合适时(假设输入有效且操作成功,并在不是这种情况下抛出异常)人们可能会转向它。

我在其他情况下避免使用out,因为它不能很好地链接,这意味着在lambda表达式中也不是很方便。

当一个对象可能会被进一步使用时,我更喜欢封装不同的值。否则,它只是另一个需要学习的类。

当有一个明显的原因同时返回两个不同的值时,我更喜欢元组,但它与上述关于新对象的用途不符合时。

最重要的是,我更喜欢一开始就只返回单个结果。

以汽车故障为例,我要么返回一个表示故障的单个对象,如果没有故障,则可以为空;要么返回一个表示汽车状态的单个对象,其中“未发现故障”可能是一个可能的值;或者最好的是,由于汽车可能有多个故障(事实上,在NCT/MOT/您国家的等效物之前似乎会出现几个昂贵的故障),因此可以返回一个可枚举或可查询的对象,让我可以迭代或查询以查找所有故障,如果没有故障,则为空(如Count == 0)。


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