【问题标题】:Try Catch blocks inside or outside of functions and error handing在函数内部或外部尝试 Catch 块和错误处理
【发布时间】:2011-01-08 08:28:06
【问题描述】:

这更像是一个通用编程问题,而不是特定于语言的问题。我已经看到了几种尝试和捕获的方法。

一个是您对所需的数据进行任何预处理,使用适当的参数调用函数并将其包装到 try/catch 块中。

另一种是简单地调用一个函数来传递数据并依赖函数内的try catch,如果发生错误,函数会返回一个真/假标志。

第三个是函数外部和内部的 try catch 组合。但是,如果函数 try catch 捕获了某些东西,它会抛出另一个异常,以便函数外部的 try catch 块捕获。

对这些错误控制方法的优缺点有什么看法,或者是否有公认的标准?我的谷歌忍者技能让我无法找到这方面的准确数据。

【问题讨论】:

    标签: exception error-handling try-catch


    【解决方案1】:

    我遇到的异常处理没有真正的硬性规定,但我有一些我喜欢应用的一般经验法则。

    即使在系统的较低层处理了一些异常,请确保在系统的入口点有一个 catch all 异常处理程序(例如,当您实现一个新线程(即 Runnable)、Servlet、MessasgeDrivenBean、服务器插座等)。这通常是做出最终决定的最佳位置,即您的系统应该如何继续(记录并重试、退出错误、回滚事务)

    永远不要在 finally 块中抛出 execption,您将丢失原始异常并用不重要的错误掩盖真正的问题。

    除此之外,这取决于您正在实现的功能。您是否处于循环中,应该重试其余项目还是中止整个列表?

    如果您重新抛出异常,请避免记录,因为它只会给您的日志添加噪音。

    【讨论】:

      【解决方案2】:

      我认为考虑这个问题的最佳方式是从程序状态的角度来考虑。您不希望失败的操作破坏程序状态。 This paper 描述了“异常安全”的概念。

      一般来说,您首先需要决定一个函数需要保证什么级别的异常安全。级别是

      • 基本保证
      • 强有力的保证
      • 无投掷保证

      basic Guarantee 简单来说就是在遇到异常或其他错误时,不会泄露任何资源,强保证就是说程序状态回滚到异常之前,nothrow 方法永远不会抛出异常。

      当发生意外的运行时故障时,我个人会使用异​​常。意外对我来说意味着在正常的操作过程中不应该发生这样的故障。运行时意味着错误是由于我无法控制的某些外部组件的状态,而不是由于我的逻辑错误。我使用 ASSERT() 来捕获逻辑错误,并使用布尔返回值来处理预期错误。

      为什么? ASSERT 不会编译到发布代码中,因此我不会让我的用户为我自己的失败进行错误检查。这就是单元测试和断言的用途。布尔值,因为抛出异常会给出错误消息。例外情况也可能很昂贵。如果我在应用程序执行的正常过程中抛出异常,那么我不能使用 MS Visual Studio 调试器出色的“抛出时捕获”异常功能,我可以让调试器在 any 抛出异常,而不是仅在未处理(崩溃)异常时停止的默认设置。

      要查看基本保证的 C++ 技术,请搜索“RAII”(资源获取即初始化)。这是一种将资源包装在对象中的技术,该对象的构造函数分配资源,而析构函数则释放资源。由于 C++ 异常展开堆栈,它保证在面对异常时释放资源。您可以使用此技术在遇到异常时回滚程序状态。只需给一个对象添加一个“Commit”方法,如果一个对象在销毁之前没有提交,则在析构函数中运行“Rollback”操作恢复程序状态。

      【讨论】:

      • 什么是 NoThrow 保证?这是 C++ 的事情吗?在 Java 和 Python 中,有许多错误是您不能(也应该)不尝试捕获的。
      • NoThrow 是保证其他保证成为可能的绝对必要保证。析构函数和反初始化函数(例如 C 的“Free()”)必须是 NoThrow,这意味着它们永远不会抛出异常。 RollBack() 函数也不应该抛出异常。如果你不能在不产生新的异常的情况下清理程序状态,这意味着你不能保证程序状态不会被破坏。有三种创建 NoThrow 操作的方法。仅由 nothrow 操作组成它们:从逻辑上保证它们不能抛出,或者放置一个可以吞下异常的 try-catch。
      • 也就是说,我知道有三个例外情况,您根本无法一直阻止。它们是 ThreadAbort 异常(这就是它们在 .NET 语言中的名称,在其他语言中可能还有其他等价物)、Out of Memory Exceptions 和 Stack Overflow 异常。所有这三个都违反了程序的抽象概念,该程序具有无限的时间、内存和可用的调用递归。对于它们中的任何一个,您都无能为力。幸运的是,您通常可以设计一个不会在绝大多数情况下受到攻击的程序。
      【解决方案3】:

      关于捕获异常的唯一问题是“是否有多种策略可以完成某事?”

      某些函数可以有意义地捕获某些异常,并在发生这些已知异常时尝试替代策略。

      所有其他异常都会被抛出。

      如果没有任何替代策略,则将简单地抛出异常。

      您很少需要一个函数来捕获(和静默)异常。异常意味着有问题。应用程序——作为一个整体——应该知道未处理的异常。它至少应该记录它们,并且可能做更多的事情:关闭或重新启动。

      【讨论】:

        【解决方案4】:

        一般来说,只有在可以实际处理的情况下才应捕获异常。

        为了记录异常而捕获异常是没有意义的。例外是应该在“顶级”捕获异常,以便可以记录它。所有其他代码都应该允许异常传播到记录它们的代码。

        【讨论】:

        • +1:只有当函数包含完成某事的替代策略时,才能处理异常。
        • 我认为捕获异常以产生有用的日志记录,比初始抛出具有更多的上下文,有时可能很有用。之后,如果无法处理异常,则重新抛出它。
        • “有更多信息”是关键。只是记录,没有额外的信息,应该等到更高的级别。
        【解决方案5】:

        我通常会考虑,作为方法的调用者,我是否可以以任何方式使用异常(例如,通过采用不同的方法从异常中恢复),或者它是否没有区别,只是在发生异常时出错。因此,在前一种情况下,我将声明抛出异常的方法,而在后一种情况下,我将在方法内部捕获它,而不用打扰调用者。

        【讨论】:

          【解决方案6】:

          应用程序中的每个“模块”都负责处理自己的输入参数。通常,您应该尽快发现问题,而不是将垃圾交给应用程序的另一部分并依靠它们来纠正。不过也有例外。有时验证输入参数基本上需要在调用者中重新实现被调用者应该做的事情(例如解析整数)。在这种情况下,通常可以尝试该操作并查看它是否有效。此外,有些操作如果不做就无法预测他们的成功。例如,您无法在写入文件之前可靠地检查您是否可以写入文件:另一个进程可能会在您检查后立即锁定该文件。

          【讨论】:

          • 这似乎没有回答这个问题。这似乎更像是对异常出现方式的回顾。
          • @S.Lott:我理解这个问题,好像 OP 想知道他是否应该依赖另一个模块来处理错误并返回布尔成功/失败值,或者让它抛出异常并捕获它们更高在调用堆栈中......也许我误解了它。它很长,我想它至少解决了其中的一部分。
          猜你喜欢
          • 2012-12-22
          • 1970-01-01
          • 1970-01-01
          • 2021-04-19
          • 2010-09-13
          • 1970-01-01
          • 2016-03-10
          • 1970-01-01
          • 2021-04-25
          相关资源
          最近更新 更多