尝试/捕捉每个班级的每一种方法?

时间:2012-06-28 00:26:37

标签: c# exception-handling

当我们在try / catch中包装一堆语句并且其中一个发出异常时,在catch中我们无法知道哪个语句导致了异常(ex.stacktrace显示了我们当前的方法(doit) ),它的调用者,调用者的调用者等,但是do1或do2都没有:

function doit() {
   try {
     do1();
     do2();
     [...]
   }
   catch (Exception ex) {
     // what failed?
   }
}

一般来说,我已经采取了包装所有陈述和重新抛出,有点像:

private void do1() {
  try {
     // do whatever
  } catch(Exception e) {
     // write to my error log
     throw new Exception("do1: " + e.Message, e.InnerException);
  }
}

在我的日志中留下了一串痕迹,并使链条可用于上游。当然,问题是我必须使用这种代码包装我编写的每个方法。

某事告诉我,我对此感到愚蠢。什么是正确的方法?

4 个答案:

答案 0 :(得分:7)

好的,这很难做到正确,因为异常处理是一个非常敏感的话题,过去人们就如何正确地进行宗教战争。

首先:既不使用空捕获(try { ... } catch { ... }),也不使用catch(Exception ex)。 Exception派生类的唯一目的是为您提供有关发生的异常类型的丰富信息,以便您可以在异常处理程序中执行有意义的操作(如果线程崩溃重新启动它,如果数据库连接失败非永久性再次尝试,然后失败,等等。)

人们倾向于使用catch-all处理程序来处理其代码的最外层部分以记录未捕获的异常,这是好的,但无论如何,您应该提示用户或重新抛出异常(使用{{1} },而不是throw - 关于此问题也有很多讨论。)

基本上,您没有以编程方式关注异常发生的地方 。 您可以处理它,也可以不处理它。如果你无法处理它,那你就不会抓住它。

附录:最重要的原因是“如果你可以做些什么,就这样做,否则你不敢触摸那个例外”的理念是无声地捕捉异常(无论是否记录)可以导致真正难以发现的错误。将它们推送到日志文件可能是不够的,因为在实时系统中,您可能无法获得完全注释的堆栈跟踪(包含行号和所有内容)。

附录2 :例如,需要整数输入的文本框。如果用户提供的字符串无法有效处理输入,则可能会抛出转换异常,捕获该特定异常并将文本框重置为其旧值,并可能通知用户错误输入。或者,您的程序可能只是因为异常而死(设计不好,您可以从该异常中恢复)或者静默地继续显示错误的输入,但仍然使用旧值(设计错误,程序误导)。

答案 1 :(得分:4)

如果您真的在这样做时出售(其他人已经说明了为什么这已经很糟糕),请使用面向方面编程方法。这将使您的生活变得更加轻松,并减少您最终编写和维护的代码量。

看看PostSharp,它为您提供了一个框架,允许您使用将为您生成此样板错误处理的属性修饰方法,类或命名空间。

答案 2 :(得分:3)

@Branko在评论中将其钉住:异常的堆栈跟踪显示抛出异常的点,而不是它被捕获的位置。

+1 @ ChaosPandion的评论:这是一个非常非常糟糕的主意。

答案 3 :(得分:1)

在我看来,{p> trycatch可能是设计最差的现代编程机制。我们不再有能力处理错误;如果发生单个异常,我们必须使整个过程失败,除非我们做一些可怕的事情,比如单独尝试每个语句。不再有恢复的选择,只能尽可能优雅地失败。

到目前为止,我发现的最佳模式是将每个用户事件包装在try / catch中(使用方法,而不是每次都进行显式尝试)。例如:

public static class Defines
{
   public static bool TryAction(Action pAction)
   {
      try { pAction(); return true; }
      catch(Exception exception) { PostException(exception); return false; }
   }
}

...

private void DoSomething(int pValue)
{
   ...
}

private void MyControl_MyEvent(object pSender, MyEventArgs pEventArgs)
{
   Defines.TryAction(() => DoSomething(pEventArgs.Data));
}

除此之外,只需尝试编写无异常代码。只有在您很可能遇到异常时才使用显式try,并且想要做的不仅仅是优雅地失败。