提问人: 提问时间:1/12/2009 更新时间:6/24/2011 访问量:2553
您喜欢在哪里捕获异常,为什么?
Where do you like to catch exceptions and why?
问:
您喜欢在哪里捕获异常,为什么?
我有兴趣看看人们发现在哪里放置他们的尝试/捕获块是有用的,希望可能会出现一些一般模式。我将用 C++ 发布我的两个示例答案,但任何语言都可以。
请每个答案一个位置和原因。谢谢。
答:
我总是在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() );
}
}
我喜欢在控制器中捕捉处理从视图触发的事件的处理程序。如果异常提供了强有力的安全保证,这是一个有用的捕获点,因为它足够高,可以报告错误,并且处理程序通常是原子的,并且与用户刚刚完成的事情相关,因此希望他们能够弄清楚发生了什么。
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() );
}
}
评论
在 C# 和 Java 中,我宁愿不捕获异常。如果我无法避免它,我会立即重新抛出运行时异常。
我将始终使用最贪婪的捕获块,该块具有所需的最大范围,但始终尽可能使用最具体的异常类型。由于在 99.99% 的情况下,catch 块的结果是另一次投掷,因此我尝试将完整方法保留在 try 块内。
两个地方:
- 在所有事件处理程序中
- 捕获可以执行有用操作的任何位置,例如使用对调试有用的信息来补充异常。通常,将使用此信息创建并引发新异常。请注意,应将此新异常的 InnerException(或等效 non-.NET)设置为原始异常的 InnerException,以便保留堆栈跟踪等内容。
我试着只捕捉那些我可以处理的异常。
我讨厌这样的代码:
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,我只捕获我能处理的错误。当输入不符合我的期望时,我很乐意抛出未捕获的异常。
这个想法是我不希望错误被记录或记录。我想修复它。
评论
不要抓住任何你没有准备好和无法处理的东西。
因此,请准备好顶级异常处理,以您喜欢的方式轰炸应用程序,以应对意外异常,然后只捕获所需的内容(尽可能接近它可能发生的位置)以获得所需的功能。
您应该只做以下两件事之一:实际做一些事情来解决/解决问题,或者重新抛出一个更具描述性的异常,该异常将捕获的异常作为其 .innerException
编辑:如果你需要一个块(例如,释放你在代码中分配的东西),并且你对任何可能弹出的异常没有任何有用的关系,同样的逻辑适用:只是不要处理它们。相反,请使用 a 将异常重新引发到更高的级别,同时保持所有异常信息不变。(或者干脆省略 catch 块,我认为/希望它做同样的事情?finally
catch { throw; }
评论
我喜欢尝试将我的异常处理代码与其他代码分开,因此我通常会创建一个执行实际逻辑的帮助程序方法,而外部方法只处理异常处理。我一直认为这给了代码一个干净的外观,并使其更具可读性。
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
}
在 Delphi Windows 应用程序中,应用程序的主要消息处理循环(即在调用堆栈的底部)通过显示消息框来处理异常。在我看来,这是处理异常的最佳位置。
通过在自己的方法中捕获异常,其唯一目的是向用户显示消息框,则拒绝调用方法的任何代码,因为该代码知道异常确实发生了,并且该方法实际上失败了。
只有在以下情况下,我才会处理异常:
- 我需要做一些清理(如数据库回滚),在这种情况下,我会在清理完成后重新引发异常。
- 我还有一些信息要添加到例外中。
- 尽管有例外,但我的方法可以成功实现其目的。
(在我开始之前:我是一个 Java 人)
我推荐:
- 从异常源中找到调用链上最近的点,您可以在其中正确处理异常 - 即采取纠正措施、事务/操作的信号失败等(日志记录本身不应被视为处理异常) 处理程序和抛出器之间的所有方法都应忽略异常。优先选择未检查的异常而不是已检查的异常,这样异常甚至不会出现在所有这些中间方法中。
- 层边界和 API 应指定它可以使用选中的异常来引发的异常,因为处理这些异常是使用它的客户端层/代码的协定的一部分。
- 编写一个带有方法的异常处理程序类,并最初将其发布给团队,并确保每个人都使用它来处理异常。根据不断变化的异常处理方案,稍后继续添加重载的“handle”方法,以便只需要修改处理程序。
handle(Exception e)
- 永远记住在进行接球和投掷时链接异常。这可确保报告异常的完整原因。
- 切勿多次记录相同的异常跟踪。这使得使用日志文件进行调试变得非常困难。
- 顶级方法应该有一个 catch 子句,该子句将捕获系统可能引发的任何异常。这可以防止我们的内部信息泄露给外界,如果上帝保佑,生产环境中出现问题。这更像是一种安全要求。
上一个:为什么异常处理不好?[已结束]
下一个:C 语言中的错误日志记录#
评论