5

当我们将一堆语句包装在 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

4 回答 4

9

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

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

人们倾向于在其代码的最外层使用一个包罗万象的处理程序来记录未捕获的异常,这还不错,但无论如何你应该提示用户或重新抛出异常(使用throw, 不是throw ex- 有很多也对此进行讨论)。

基本上,您根本不会以编程方式关心异常发生的位置。你可以处理它,或者你不能。如果你不能处理它,那么你就不会抓住它。

附录:“如果你能做点什么,就去做,否则你不敢碰那个异常”的最重要的原因是静默捕获的异常(无论是否记录)会导致非常难以发现的错误. 仅将它们推送到日志文件可能还不够,因为在实时系统中,您可能无法获得完全注释的堆栈跟踪(带有行号和所有内容)。

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

于 2012-06-28T01:32:50.823 回答
4

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

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

于 2012-06-28T00:57:02.430 回答
3

@Branko 在评论中指出了这一点:异常的堆栈跟踪显示了引发异常的点,而不是捕获异常的点。

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

于 2012-06-28T00:35:09.660 回答
2

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

到目前为止,我发现的最佳模式是将每个用户事件包装在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仅当您很可能遇到异常并且想要做的不仅仅是优雅地失败时,才使用显式s。

于 2012-06-28T02:31:02.857 回答