【问题标题】:How to indicate that a method was unsuccessful如何指示方法不成功
【发布时间】:2010-09-14 18:41:40
【问题描述】:

我有几种类似的方法,例如。 CalculatePoint(...) 和 CalculateListOfPoints(...)。有时,他们可能不会成功,需要向调用者表明这一点。对于返回通用列表的CalculateListOfPoints,我可以返回一个空列表并要求调用者检查它;但是 Point 是一个值类型,所以我不能在那里返回 null。

理想情况下,我希望方法“看起来”相似;一种解决方案可能是将它们定义为

public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);

或者返回一个点?对于CalculatePoint,返回null表示失败。不过,这意味着必须转换回不可为空的类型,这似乎太过分了。

另一种方法是返回 Boolean boSuccess,将结果(Point 或 List)作为 'out' 参数,然后调用它们 TryToCalculatePoint 或其他东西...

什么是最佳实践?

编辑:我不想使用异常来控制流量!有时会失败。

【问题讨论】:

  • 只是一个设计点...如果您要返回一个集合,如果您不成功,请不要返回 null,只需返回一个空集合。我投票支持 TryCalculate 返回一个布尔值。
  • 由于参数超出范围而未完成的方法不是“流控制”,这是一种意外和异常情况。
  • @Will:我同意,这就是我所说的空列表的意思。 @Rich:同意;但是参数可以很好,只是可能没有合适的返回点。
  • @Joel:那么您可能需要尝试模式,但是,对于您在问题中列出的内容,您应该抛出异常。

标签: c# methods return-value


【解决方案1】:

就个人而言,我想我会使用与 TryParse() 相同的想法:使用 out 参数输出实际值,并返回一个布尔值,指示调用是否成功

public bool CalculatePoint(... out Point result);

我不喜欢将异常用于“正常”行为(如果您希望该功能不适用于某些条目)。

【讨论】:

  • @lucasbfr:+1,如果您提供的 DoX 可能会引发异常,您可以弄清楚,请提供 TryX。
  • @sixlettervariables:我同意,我不想更改方法的名称,因为他没有问,但这是一个很好的做法
  • 虽然 TryParse 概念有时会变得乏味,因为您总是需要声明一个变量,但我已经习惯了它并且总是更喜欢它而不是抛出异常的版本。但是,只要您可以只返回 null,我就会这样做,而不是 TryParse 方法。
  • 如果您要这样做,我会将方法名称更改为 Try calculate point。
【解决方案2】:

他们为什么会失败?如果是因为调用者所做的事情(即提供的参数),那么抛出 ArgumentException 是完全合适的。避免异常的 Try[...] 方法很好。

我认为提供引发异常的版本是个好主意,这样那些期望他们将始终提供良好数据的调用者会在他们出错时收到适当强的消息(即异常)。

【讨论】:

    【解决方案3】:

    另一种选择是抛出异常。但是,您通常只想在“异常情况”中抛出异常。

    如果失败案例很常见(并非例外),那么您已经列出了您的两个选项。 编辑:您的项目中可能有一个约定,即如何处理此类非异常情况(是否应该返回成功或对象)。如果没有现有约定,那么我同意 lucasbfr 并建议您返回成功(这与 TryParse(...) 的设计方式一致)。

    【讨论】:

      【解决方案4】:

      如果失败是由于特定原因,那么我认为返回 null 或 bool 并具有 out 参数是可以的。但是,如果无论失败如何都返回 null ,那么我不推荐它。异常提供了一组丰富的信息,包括为什么某事失败的原因,如果你得到的只是一个空值,那么你怎么知道它是因为数据错误,你已经用完了内存或其他一些奇怪的行为。

      即使在 .net 中,TryParse 也有 Parse 兄弟,因此您可以根据需要获取异常。

      如果我提供了一个 TrySomething 方法,我也会提供一个在失败时抛出异常的 Something 方法。然后由调用者决定。

      【讨论】:

        【解决方案5】:

        我使用的模型与 MS 用于各种类的 TryParse 方法的模型相同。

        您的原始代码:
        public Point CalculatePoint(... out Boolean boSuccess);
        public List CalculateListOfPoints(... out Boolean boSuccess);

        会变成 public bool CalculatePoint(... out (or ref) Point CalculatedValue);
        public bool CalculateListOfPoints(... out (or ref) List CalculatedValues);

        基本上你将成功/失败作为返回值。

        【讨论】:

          【解决方案6】:

          总结一下,您可以采取以下几种方法:

          1. 当返回类型是值类型时,比如Point,使用C#的Nullable特性,返回Point? (又名 Nullable),这样你仍然可以在失败时返回 null
          2. 失败时抛出异常。关于什么是“异常”和不是“异常”的整个争论/讨论是一个有争议的问题,它是您的 API,您决定什么是异常行为。
          3. 采用类似于 Microsoft 在 Int32 等基本类型中实现的模型,提供 CalculatePoint 和 TryCalculatePoint(int32.Parse 和 int32.TryParse),并有一个 throw 和一个 return bool。
          4. 从您的方法中返回一个通用结构,该结构具有两个属性:bool Success 和 GenericType Value。

          取决于场景,我倾向于使用返回 null 或抛出异常的组合,因为它们对我来说似乎“最干净”并且最适合我工作的公司的现有代码库。所以我个人的最佳实践是方法 1 和 2。

          【讨论】:

            【解决方案7】:

            这主要取决于您的方法的行为及其用法。

            如果失败很常见且不严重,则让您的方法返回一个布尔值,指示其成功,并使用 out 参数来传达结果。在哈希中查找键,在没有数据可用时尝试在非阻塞套接字上读取数据,所有这些示例都属于该类别。

            如果失败是意料之外的,直接返回结果并用异常传达错误。以只读方式打开文件、连接到 TCP 服务器是不错的选择。

            有时两种方式都有意义...

            【讨论】:

              【解决方案8】:

              返回 Point.Empty。当您想要检查结构创建是否成功时,返回一个特殊字段是一种 .NET 设计模式。尽可能避免 out 参数。

              public static readonly Point Empty
              

              【讨论】:

              • 为什么要避免out参数?事实上,我确实会尽量避免它们,但那是因为我发现它们的意图不太清楚。
              • 输出参数(和其他类型的指针)对于初学者来说可能很棘手。这是语义错误的典型来源。微软还建议避免它:msdn.microsoft.com/en-us/library/ms182131(VS.80).aspx
              【解决方案9】:

              我正在试验的一个模式是返回一个 Maybe。它具有 TryParse 模式的语义,但与 null-return-on-error 模式的签名相似。

              我还没有说服任何一种方式,但我提供它以供您集体考虑。它确实具有不需要在方法调用之前定义变量以在方法的调用站点保存 out 参数的好处。也可以使用 ErrorsMessages 集合来扩展它,以指示失败的原因。

              Maybe 类看起来像这样:

              /// <summary>
              /// Represents the return value from an operation that might fail
              /// </summary>
              /// <typeparam name="T"></typeparam>
              public struct Maybe<T>
              {
                  T _value;
                  bool _hasValue;
              
              
                  public Maybe(T value)
                  {
                      _value = value;
                      _hasValue = true;
                  }
              
                  public Maybe()
                  {
                      _hasValue = false;
                      _value = default(T);
                  }
              
              
                  public bool Success
                  {
                      get { return _hasValue; }
                  }
              
              
                  public T Value
                  {
                      get 
                          { // could throw an exception if _hasValue is false
                            return _value; 
                          }
                  }
              }
              

              【讨论】:

                【解决方案10】:

                我会说最佳实践是返回值表示成功,exception 表示失败。

                在您提供的示例中,我认为没有理由在发生故障时不应使用 exceptions

                【讨论】:

                • 在非异常情况下不应使用异常。您需要在这里小心。
                • 我不喜欢将异常用于“正常”行为(如果您希望该功能不适用于某些条目)。
                • @Kevin:像无法完成一个方法这样的例外情况?
                • @lucasbfr:请说明您认为此方法失败的地方是“正常”行为。我在问题中根本没有看到这个措辞。
                • 游戏、服务器和图形应用程序呢?他的例子至少表明了一个游戏或一个图形应用程序。例外很慢:非常非常慢。始终为潜在开发人员提供避免异常的方法。
                【解决方案11】:

                在某些情况下(尤其是在编写服务器时)使用异常是个坏主意。您将需要两种风格的方法。还要查看字典类以了解您应该做什么。

                // NB:  A bool is the return value. 
                //      This makes it possible to put this beast in if statements.
                public bool TryCalculatePoint(... out Point result) { }
                
                public Point CalculatePoint(...)
                {
                   Point result;
                   if(!TryCalculatePoint(... out result))
                       throw new BogusPointException();
                   return result;
                }
                

                两全其美!

                【讨论】:

                • 我同意 try 想法在某些情况下是一个不错的想法,但是您关于在服务器中引发异常的说法非常错误,我不知道从哪里开始。
                • 我没有说永远不会抛出异常:)。只有当你绝对需要时。如果 XMPP 服务器上有 10000 个活跃用户,您将如何处理 XML 解析错误?为庞大的用户群编写 TCP 服务器与仅保持几毫秒连接的 Web 应用程序大不相同。
                • @Jonathon:说服务器不应该抛出异常是完全错误的。它向我表明您正在使用异常进行流量控制,这更加错误。
                • @rich-b 不使用它进行流量控制。在我的服务器中有一个 patricia trie,用于我的服务器获得的每个节 (5000 p/m)。如果找不到节点,则通过 S2S 将其发送到另一个 xmpp 服务器。我应该在这里抛出异常吗?还是返回false?
                【解决方案12】:

                bool TrySomething() 至少是一种实践,它适用于 .net 的解析方法,但我认为我一般不喜欢它。

                抛出异常通常是一件好事,尽管它不应该用于您希望在许多正常情况下发生的情况,并且它具有相关的性能成本。

                在大多数情况下,当您不希望出现异常时,尽可能返回 null 是可以的。

                然而 - 您的方法有点程序化 - 创建类似 PointCalculator 类的东西怎么样 - 将所需数据作为构造函数中的参数?然后在其上调用 CalculatePoint,并通过属性(Point 和 Success 的单独属性)访问结果。

                【讨论】:

                • 这真的是个好主意吗?我不想为每两个方法创建一个类,我最终会得到数百个。
                • @Tobj0rn:我倾向于同意,我认为这是他的代码库中一个更大问题的症状。
                【解决方案13】:

                当发生预期的事情时,您不希望抛出异常,因为@Kevin 声明异常是针对特殊情况的。

                您应该返回“失败”的预期值,通常 null 是我选择的错误返回。

                您的方法的文档应告知用户当数据不计算时会发生什么。

                【讨论】:

                  【解决方案14】:

                  我们曾经编写了一个完整的框架,其中所有公共方法要么返回 true(成功执行),要么返回 false(发生错误)。如果我们需要返回一个值,我们使用输出参数。与流行的看法相反,这种编程方式实际上简化了我们的很多代码。

                  【讨论】:

                  • 天哪。如果您使用具有异常处理的语言执行此操作,请发布代码。我将为 TDWTF 带来一个新的头版故事。
                  【解决方案15】:

                  使用 Point,您可以在失败的情况下将 Point.Empty 作为返回值发回。现在所有这一切都是为 X 和 Y 值返回一个 0 的点,所以如果这可以是一个有效的返回值,我会远离它,但如果你的方法永远不会返回一个 (0,0) 点,那么你就可以使用它了。

                  【讨论】:

                    【解决方案16】:

                    对不起,我只记得 Nullable 类型,你应该看看那个。不过我不太确定开销是多少。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-11-16
                      • 1970-01-01
                      • 2019-07-12
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多