【问题标题】:Throw Exception VS Return Error within a Try,Catch,Finally在 Try,Catch,Finally 中抛出异常 VS 返回错误
【发布时间】:2010-11-09 18:02:40
【问题描述】:

我很确定我已经知道答案,但我仍然很好奇在处理 Try,Catch,Finally 块中的错误的意见是什么——但是当你重复自己时。

顺便说一句 - 我不是在谈论用户输入 - 而是以它为例,因为它清晰而简短

考虑一下这段代码...

try {    
    if (success) {
        return someSuccessMessage;
    }
    else {
        logError("User input not correct format");
        return someErrorMessage; // repeats itself
    }
}
catch (Exception ex) {
    logError(ex.Message);
    return someErrorMessage; // repeats itself
}

假设我们有一个函数,如果它失败了,我们想返回一条错误消息,因为异常无关紧要——我们的函数没有成功,用户不需要任何额外的细节。

我一直认为,如果您可以处理错误,请避免异常——因为它不再是例外,但我想知道关于避免重复自己的意见......您可以执行以下操作避免重复自己...

try {    
    if (success) {
        return someSuccessMessage;
    }
    else {
        throw new Exception("User input not correct format");
    }
}
catch (Exception ex) {
    logError(ex.Message);
    return someErrorMessage;
}

这不是最好的例子,但为了简洁起见,我想强调重复代码。

已知异常会导致性能损失,但对于这种情况有什么想法?

【问题讨论】:

标签: exception try-catch try-catch-finally


【解决方案1】:

我质疑这里的关注点分离。除非此函数是 UI 的一部分,否则它不应关注错误消息。它应该改为抛出异常。此方法的调用者(如果它是 UI 的一部分)可能希望生成一条错误消息以供显示。如果调用者是一个 Web 服务,那么它会想要产生一个 SOAP 错误,它可能不会使用相同的消息(如果它根本使用任何消息)。

我还强烈建议您记录 ex.ToString() 而不是 ex.Message。

【讨论】:

    【解决方案2】:

    IMO,只有在可纠正情况之外才应抛出异常。用户输入的格式不正确是已知的,不应抛出任何异常。

    将异常视为灾难性的(数据中心着火、地震等)。这样,您将看到处理“常规错误”和“异常”之间的区别。

    是的,抛出和捕获异常会消耗大量性能,最好避免它们。

    【讨论】:

      【解决方案3】:

      如果您觉得重复自己是个问题,请将重复的代码提取到一个函数中。

      error_code_t fail (string message) {
          logError(message);
          return someErrorMessage;
      }
      
      // ...
      
      try {    
          if (success) {
              return someSuccessMessage;
          }
          else {
              return fail("User input not correct format");
          }
      }
      catch (Exception ex) {
          return fail(ex.Message);
      }
      

      说实话,我不担心在同一个函数中重复几行。

      【讨论】:

      • 我不同意最后一句话。乍一看,删除几行重复可能看起来“不那么重要”,但想象一下 10 种不同方法中的相同“轻微重复”。复制是邪恶的。正如圣埃克苏佩里所说:完美不是没有什么可添加的,而是没有什么可删除的。
      • @MichaelZilbermann “复制是邪恶的。”——并非如此。正如许多伟大的程序员所指出的那样,“重复比错误的抽象要好”。您不应该一有机会就删除重复数据,而应该在它被证明是一个问题之后。
      【解决方案4】:

      在您的情况下,我只会返回错误消息(第一个示例),因为抛出异常只是为了捕获它下面的 3 行似乎有点奇怪。

      完全不同的是,我通常会尽可能避免返回错误代码 - 当我遇到错误情况时,我会通过异常并尽可能在最高级别捕获它。通过这种方式,代码不会到处都是错误处理,并且更容易看到业务逻辑。在你的情况下(如果你当然控制它)返回成功的方法可能会在失败的情况下抛出异常,你根本不必问这个问题:)

      C# 中的异常确实很昂贵,因此不应滥用它们。话虽如此,当你遇到错误时,50ms 左右的性能损失通常是无关紧要的,所以我倾向于使用它们来保持代码的整洁。

      【讨论】:

      • 我应该发布一个更复杂的例子——这个简短的例子只是为了说明它——我说的是一个更大的例子。尽量避免重复错误处理代码。谢谢你的回答
      【解决方案5】:

      我同意您在示例中的逻辑,但是您认为您在异常处理块与程序测试中处理的是什么异常?我怀疑您的异常处理块实际上是“以防万一发生”。所以它真的归结为异常处理规则。

      如果您不需要处理异常并且它不跨越不同的架构边界,请不要处理它。如果它位于组件边界的边缘,您可能需要将其包装起来,将原件放在内部异常中。

      如果功能是从代码中调用的,您可能希望使用某种状态表示来测试结果,例如响应(HRESULT 就是一个很好的例子。0 == SUCCESS,!= 0 == failure)或使用例外。

      测试程序错误或组件故障是您将使用异常的地方,如果您在 UI 上验证来自用户的输入,您可能希望简单地使用逻辑并返回状态代码来帮助将错误传达给用户。

      最后也要考虑本地化。如果您通过系统将英文错误消息向上传播,并将其呈现给您的法语用户,这将不会有用,并且您不想在您的 UI 上开始解析字符串以生成法语版本,那么例外是解决问题的方法只要异常的有效负载有足够的信息来生成有用的错误消息,以便用户采取纠正措施。

      在组件之间存在紧密耦合的情况下使用状态代码,并且调用组件知道在不同的状态条件下要做什么。

      顺便说一句,您可能想记录堆栈跟踪以及使用 ToString() 的消息,因为它将为您提供更多有用的信息来解决问题。

      HTH

      【讨论】:

      • 感谢您的回答-顺便说一句-这不是真正的代码-只是为了说明这一点:)
      【解决方案6】:

      在这种情况下,我实际上会说 try/catch 是不必要的,因为您的 if 足以处理您的错误

      但最下面的是我认为应该用于更复杂情况的样式

      【讨论】:

        猜你喜欢
        • 2011-03-28
        • 1970-01-01
        • 1970-01-01
        • 2017-10-03
        • 2014-04-27
        • 1970-01-01
        • 2011-03-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多