尝试在函数内部或外部捕获块并处理错误

Try Catch blocks inside or outside of functions and error handing

提问人:CogitoErgoSum 提问时间:1/23/2010 最后编辑:Josh CorreiaCogitoErgoSum 更新时间:5/3/2023 访问量:22937

问:

这与其说是特定于语言,不如说是一个通用编程问题。我见过几种尝试和捕捉的方法。

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

另一种是简单地调用一个函数传递数据并依赖于函数中的 try catches,如果发生错误,该函数将返回 true/false 标志。

第三种是与函数外部和内部的尝试捕获的组合。但是,如果函数 try catch 捕获了某些内容,则它会为要捕获的函数外部的 try catch 块抛出另一个异常。

关于这些错误控制方法的优缺点,或者是否有公认的标准,您有什么想法吗?我在谷歌上搜索忍者的技能使我无法找到这方面的准确数据。

异常 错误处理 try-catch

评论


答:

2赞 Mehrdad Afshari 1/23/2010 #1

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

评论

0赞 S.Lott 1/23/2010
这似乎没有回答这个问题。这似乎更像是对异常可能出现的方式的回顾。
0赞 Mehrdad Afshari 1/23/2010
@S.Lott:我把这个问题理解为OP想知道他是否应该依赖另一个模块来处理错误并返回布尔成功/失败值,或者让它抛出异常并在调用堆栈中捕获它们......也许我误解了。它很长,我想它至少解决了其中的一部分。
0赞 Fabian Steeg 1/23/2010 #2

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

25赞 John Saunders 1/23/2010 #3

通常,只有当异常可以实际处理时,才应捕获该异常。

除了记录异常之外,没有其他目的捕获异常是没有意义的。例外情况是,应在“顶层”捕获异常,以便可以记录它。所有其他代码都应允许将异常传播到将记录异常的代码。

评论

1赞 S.Lott 1/23/2010
+1:而且,只有当函数包含完成某事的替代策略时,才能处理异常。
2赞 extraneon 1/23/2010
我认为捕获异常以产生有用的日志记录,具有比初始抛出更多的上下文,有时是有用的。之后,如果无法处理异常,请重新抛出它。
1赞 John Saunders 1/23/2010
“提供更多信息”是关键。只是日志记录,没有其他信息,应该等待到更高的级别。
0赞 S.Lott 1/23/2010 #4

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

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

将引发所有其他异常。

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

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

4赞 David Gladfelter 1/23/2010 #5

我认为考虑这个问题的最好方法是从程序状态的角度来看。您不希望失败的操作损坏程序状态。本文描述了“异常安全”的概念。

通常,您首先需要确定函数需要保证的异常安全级别。级别是

  • 基本瓜南特
  • 强力保障
  • NoThrow 保证

基本保证只是意味着在遇到异常或其他错误时,不会泄露任何资源,强保证表示程序状态回滚到异常之前,并且 nothrow 方法永远不会抛出异常。

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

为什么?ASSERT 不会编译到发布代码中,因此我不会为用户提供错误检查的负担。这就是单元测试和 ASSERTS 的用途。布尔值,因为抛出异常可能会给出错误的消息。例外情况也可能代价高昂。如果我在应用程序执行的正常过程中抛出异常,那么我就不能使用 MS Visual Studio 调试器出色的“Catch on thrown”异常功能,我可以让调试器在抛出任何异常时中断程序,而不是仅在未处理(崩溃)异常时停止的默认值。

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

评论

0赞 S.Lott 1/23/2010
什么是 NoThrow 保证?这是 C++ 的东西吗?在 Java 和 Python 中,有许多错误你不能(也不应该)尝试捕捉。
0赞 David Gladfelter 1/23/2010
NoThrow 是实现其余保证的绝对必要的保证。析构函数和反初始值设定器(例如 C 的 'Free()'))必须是 NoThrow,这意味着它们永远不会抛出异常。RollBack() 函数也不应该抛出。如果无法在不生成新异常的情况下清理程序状态,则意味着无法保证程序状态不会损坏。有三种方法可以创建 NoThrow 操作。仅对它们进行 nothrow 操作:从逻辑上讲保证它们不能抛出,或者放置一个吞噬异常的 try-catch。
0赞 David Gladfelter 1/23/2010
也就是说,我知道有三个例外,你根本无法一直阻止。它们是 ThreadAbort 异常(这是它们在 .NET 语言中的名称,在其他语言中可能还有其他等效异常)、内存不足异常和堆栈溢出异常。这 3 个都违反了程序的抽象概念,该程序具有无限的时间、内存和调用递归。你对它们中的任何一个都无能为力。幸运的是,在绝大多数情况下,您通常可以设计一个程序来避免受到这些程序的打击。
1赞 Michael Barker 1/23/2010 #6

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

Even if some exceptions are handled at the lower layer of your system make sure there is a catch all exception handler at the entry point of your system (e.g. When you implement a new Thread (i.e. Runnable), Servlet, MessasgeDrivenBean, server socket etc). This is often the best place to make the final decision as how your system should continue (log and retry, exit with error, roll back transaction)

Never throw an execption within a finally block, you will lose the original exception and mask the real problem with an unimportant error.

Apart from that it depends on the function that you are implementing. Are you in a loop, should the rest of the items be retried or the whole list aborted?

If you rethrow an exception avoid logging as it will just add noise to your logs.