5

我们正在审查该公司的一个系统的异常处理,并发现了一些有趣的事情。

大多数代码块(如果不是全部)都在 try/catch 块内,并且在 catch 块内抛出了一个新的 BaseApplicationException - 这似乎来自企业库。我在这里有点麻烦,因为我看不到这样做的好处。(任何时候发生异常都会抛出另一个异常)其中一位已经使用该系统一段时间的开发人员说这是因为该类负责发布异常(发送电子邮件和类似的东西),但他不太确定。在花了一些时间浏览代码之后,我很有信心地说,它所做的就是收集有关环境的信息,而不是发布它。

我的问题是: - 将所有代码包装在 try { } catch { } 块中而不是抛出新异常是否合理?如果是,为什么?有什么好处?

我个人的看法是,使用 HttpModule,注册 Application 事件的 Error 事件,并在模块内做必要的事情会容易得多。如果我们沿着这条路走,我们会错过什么吗?有什么缺点吗?

非常感谢您的意见。

4

6 回答 6

10

从不1 catch (Exception ex)第 2期。您无法处理可能捕获的所有不同类型的错误。

如果您无法处理或提供额外信息(供后续异常处理程序使用),切勿3捕获异常派生类型。显示错误消息处理错误不同。

从我的头顶来看,有几个原因:

  • 捕捉和重新投掷是昂贵的
  • 你最终会丢失堆栈跟踪
  • 您的代码中的信噪比较低

如果您知道如何处理特定异常(并将应用程序重置为错误前状态),请捕获它。(这就是为什么它被称为异常 处理。)

要处理未捕获的异常,请侦听适当的事件。在使用 WinForms 时,您需要倾听System.AppDomain.CurrentDomain.UnhandledException,并且 - 如果您这样做Threading-System.Windows.Forms.Application.ThreadException.对于 Web 应用程序,也有类似的机制 ( System.Web.HttpApplication.Error)。

至于在您的应用程序(非)特定异常(即throw new MyBaseException(ex);)中包装框架异常:完全没有意义,而且气味难闻。4


编辑

1 Never是一个非常苛刻的词,尤其是在工程方面,正如@Chris 在评论中指出的那样。当我第一次写这个答案时,我承认我的原则很高。

2,31

4 If you don't bring anything new to the table, I still stand by this. If you have caught Exception ex as part of a method that you know could fail in any number of ways, I believe that the current method should reflect that in it's signature. And as you know, exceptions is not part of the method signature.

于 2008-09-25T12:35:48.357 回答
2

如果我正确地阅读了这个问题,我会说实现一个 try / catch 来拦截异常(你没有提到 - 它是捕获所有异常,还是只捕获一个特定的异常?)并抛出不同的异常通常是一件坏事.

缺点:

至少你会丢失堆栈跟踪信息——你将看到的堆栈只会扩展到引发新异常的方法——你可能会在这里丢失一些好的调试信息。

如果您正在捕获异常,您将面临掩盖关键异常的风险,例如带有不太关键的异常的 OutOfMemory 或 StackOverflow,从而使进程继续运行,而它可能应该被拆除。

可能的优势:

在某些非常特殊的情况下,您可以采用一个没有太多调试价值的异常(例如从数据库返回的一些异常),并用一个添加更多上下文的异常进行包装,例如您正在处理的对象的 id。

然而,在几乎所有情况下,这是一种难闻的气味,应谨慎使用。

通常,只有在您可以在该位置执行一些现实的事情时,您才应该捕获异常——即恢复、回滚、执行 B 计划等。如果您对此无能为力,就让它沿着链条向上传递. 只有在该位置有特定且有用的数据可以增加原始异常并因此有助于调试时,您才应该捕获并抛出一个新异常。

于 2008-09-25T12:07:26.833 回答
1

我来自思想流派,应该使用 try/catch 块并且不会重新抛出异常。如果您执行的代码可能会出错,那么应该对其进行处理、记录并返回一些内容。重新抛出异常仅用于在应用程序生命周期的后期重新登录。

这是一篇关于如何使用 HttpModule 处理异常的有趣帖子:http: //blogs.msdn.com/rahulso/archive/2008/07/13/how-to-use-httpmodules-to-troubleshoot-your-asp- net-application.aspxhttp://blogs.msdn.com/rahulso/archive/2008/07/18/asp-net-how-to-write-error-messages-into-a-text-file-using-一个简单的httpmodule.aspx

于 2008-09-25T11:54:55.350 回答
1

查看ELMAH。它做你所说的。很好。

当我创建库时,我尝试始终为调用者提供减少数量的异常来处理。例如,考虑一个连接到 sql 数据库的 Repository 组件。理论上可以抛出大量异常,从 sql 客户端异常到无效转换异常。其中许多都有明确的文档记录,可以在编译时进行说明。因此,我尽可能多地捕获它们,将它们放在一个异常类型中,例如 RepositoryException,然后让该异常汇总调用堆栈。

原始异常被保留,因此可以诊断原始异常。但是我的调用者只需要担心处理单个异常类型,而不是用大量不同的 catch 块乱扔他们的代码。

当然,这也存在一些问题。最值得注意的是,如果调用者可以处理其中一些异常,他们必须在 RepositoryException 中找到根,然后打开内部异常的类型来处理它。它不如为单个异常类型使用单个 catch 块干净。不过,我认为这不是什么大问题。

于 2008-09-25T11:58:26.590 回答
0

听起来抛出的异常不应该作为异常实现。

无论如何,我会说,因为这个 BaseApplicationException 是一个通用的通用异常,所以最好抛出更特定于上下文的异常。因此,当您尝试从数据库中检索实体时,您可能需要 EntityNotFoundException。这样,当您调试时,您不必搜索内部异常和堆栈跟踪来找到真正的问题。如果此 BAseApplicationException 正在收集有关异常的信息(例如跟踪内部异常),那么这应该不是问题。

只有当我无法更接近代码中实际发生异常的位置时,我才会使用 HttpModule。您并不真正想要一个 HttModule OnError 事件,它是一个巨大的 switch 语句,具体取决于 BaseApplicationexception 的错误信息。

总而言之,当您可以给出更具体的异常来立即告诉您问题的根源时,抛出不同的异常是值得的。

于 2008-09-25T12:00:59.937 回答
0

根据我的经验,捕获异常,将错误添加到 Server (?) 对象。这将允许 .NET 做它需要做的任何事情,然后显示你的异常。

于 2008-09-25T12:02:34.910 回答