【问题标题】:Where do you like to catch exceptions and why?你喜欢在哪里捕捉异常,为什么?
【发布时间】:2010-09-30 21:28:07
【问题描述】:

您喜欢在哪里捕获异常以及为什么?

我很想看看人们认为将 try/catch 块放在哪里有用,希望可能会出现一些通用模式。我将用 C++ 发布我的两个示例答案,但任何语言都可以。

请为每个答案提供一个位置和原因。谢谢。

【问题讨论】:

  • 如果您提供选项,投票绝对应该是 wiki!

标签: design-patterns exception architecture error-handling error-reporting


【解决方案1】:

不要抓住任何你没有准备好也没有能力处理的东西。

所以,have top-level exception handling in place 在出现意外异常时以您喜欢的方式轰炸应用程序,然后只捕获您需要的东西(尽可能接近可能发生的地方)以获得所需的功能。

而且你应该只做两件事之一:实际做一些事情来解决/解决问题,或者重新抛出一个更具描述性的异常,将捕获的异常作为其innerException

编辑:如果您需要一个 finally 块(例如,释放您在代码中分配的内容)并且您对任何可能弹出相同逻辑的异常没有任何用处:干脆不要处理它们。相反,使用catch { throw; } 将异常重新抛出到更高级别,同时保持所有异常信息不变。 (或者干脆省略 catch 块,我认为/希望它做同样的事情?)

【讨论】:

  • 哦,这是在 C# 中,如果你没有注意到的话。 (其他 .NET 语言也是如此。)
【解决方案2】:

我尝试只捕获那些我可以处理的异常。

我讨厌这样的代码:

      String s="12";
      Integer i;
        try {
          i = Integer.parseInt(s);
        } catch(ParseException pe) {
          System.out.println("hihihihihihihi!!!);
        }

我特别讨厌的是,这通常会中止线程,因为在三行之后将访问 i 并假设 i != null。

然后您将阅读您的堆栈跟踪并滚动并滚动并滚动日志,直到您发现第一个导致其他一切崩溃的重大错误。

无论如何,我希望 Java 不会强迫我捕获我无法处理的异常。但我能做的是:

catch(Exception e) {
  throw new RuntimeException(e);
}

我在函数定义中声明了很多“抛出”。

我仍然梦想有一天 Eclipse 会在遇到未捕获的异常时自动在正确的行中打开一个调试器。那天,我的方法会打开正确的行。

在其他语言中,例如 Smalltalk,我只捕获我可以处理的错误。当输入不符合我的期望时,我很乐意抛出未捕获的异常。

我的想法是我不希望记录或记录错误。我想把它修好。

【讨论】:

  • 嗨 nes1983 不确定您是否知道 stackoverflow.com/questions/3066199/… - 关于您梦想有一天 Eclipse 会自动为您打开调试器的评论。这不是完全您正在寻找的东西,但会让您一路前行吗?顺便说一句,感谢您的回答。
【解决方案3】:

我总是在 main() 中添加一个 catch 作为最后的捕获:

int main( int argc, char** argv ) {
    try {
        Application application( argc, argv );
        return application.result();
    }
    catch ( const std::exception& exception ) {
        fprintf( stderr, "%s.\n", exception.what() );
    }
}

【讨论】:

    【解决方案4】:

    在 C# 和 Java 中,我更喜欢根本不捕获异常。如果无法避免,我会立即重新抛出运行时异常。

    我将始终使用所需范围最大的最贪婪的 catch 块,但始终使用最具体的异常类型。由于在 99.99% 的情况下,catch 块的结果是另一个 throw,因此我尝试将完整方法保留在 try 块中。

    【讨论】:

      【解决方案5】:

      我喜欢尝试将我的异常处理代码与其他代码分开,因此我通常创建一个辅助方法来执行实际逻辑,而外部方法只处理异常处理。我一直认为这样可以使代码看起来更干净,并且更具可读性。

      public void stuff() throws MyException
      {
          try
          {
              tryStuff();
          }
          catch (SomeLibraryException e)
          {
              logger.log("Some message happened", e);
              throw new MyException(e);
          }
      }
      
      public void tryStuff() throws SomeLibraryException
      {
         // Do things in here that could throw exceptions
      }
      

      【讨论】:

        【解决方案6】:

        我喜欢在控制器中处理处理从视图触发的事件的处理程序。如果异常提供了强有力的安全保证,这是一个有用的捕获点,因为它的级别足够高,可以报告错误,并且处理程序通常是原子的并且与用户刚刚完成的事情相关,因此希望他们能够解决发生了什么事。

        void Controller::on_edit_entity( const Entity& entity ) {
            try {
                Command::ptr command = new EditEntityCommand( entity );
                push_command( command );
            }
            catch ( const std::exception& exception ) {
                fprintf( stderr, "%s.\n", exception.what() );
            }
        }
        

        【讨论】:

        • 不要让用户自己弄明白。如果你不能处理异常——首先纠正导致它的任何问题——然后不要捕捉它,以免以后的代码因为它假设一切正常而实际上不是这样而崩溃。例如,您的代码会捕获 std::bad_alloc。
        【解决方案7】:

        在 Delphi Windows 应用程序中,应用程序的主消息处理循环(即在调用堆栈的底部)通过显示消息框来处理您的异常。在我看来,这是处理异常的最佳场所。

        通过在您自己的方法中捕获异常只是为了向用户显示一个消息框,您否认任何调用您的方法的代码知道异常实际发生并且该方法实际上失败了。

        我只会在以下情况下处理异常:

        • 我需要进行一些清理(如数据库回滚),在这种情况下,我会在清理完成后重新引发异常。
        • 我有更多信息要添加到异常中。
        • 尽管有异常,我的方法仍能成功。

        【讨论】:

          【解决方案8】:

          (开始之前:我是一名 Java 人)
          我的建议:

          1. 从异常源查找调用链上最接近的点,您可以在该点正确处理异常 - 即采取纠正措施,发出事务/操作失败的信号等(不应将日志本身视为处理异常)处理程序和抛出者之间的所有方法都应该忽略异常。更喜欢未检查的异常而不是检查的异常,这样异常甚至不会出现在所有这些中间方法中。
          2. 层边界和 API 应指定它可以使用已检查异常抛出的异常,因为处理这些异常是使用它的客户端层/代码合同的一部分。
          3. 使用handle(Exception e) 方法编写一个异常处理程序类,并最初将其发布给团队,并确保每个人都使用它来处理异常。根据不断变化的异常处理场景,稍后继续添加重载的“处理”方法,以便只修改处理程序。
          4. 在执行 catch-and-throw 时,请始终记住链接异常。这可确保报告异常的全部原因。
          5. 切勿多次记录同一异常跟踪。这使得使用日志文件进行调试变得非常困难。
          6. 顶级方法应该有一个 catch 子句来捕获系统可能抛出的任何异常。如果在生产环境中出现问题,这可以防止我们的内部信息泄露到外部世界。这更像是一项安全要求。

          【讨论】:

            【解决方案9】:

            两个地方:

            • 在所有事件处理程序中
            • catch 可以做一些有用的事情的任何位置,比如用对调试有用的信息来补充异常​​。通常,将使用此信息创建一个新异常并抛出。请注意,此新异常的 InnerException(或非 .NET 等效项)应设置为原始异常的异常,以便保留堆栈跟踪等内容。

            【讨论】:

              猜你喜欢
              • 2010-09-11
              • 1970-01-01
              • 2016-09-07
              • 2021-01-27
              • 1970-01-01
              • 1970-01-01
              • 2011-11-16
              • 2017-09-26
              • 2014-07-11
              相关资源
              最近更新 更多