19

自从 Microsoft 推出应用程序块以来,我一直在遇到使用异常处理应用程序块的人。我最近仔细观察了自己,并将基本功能总结如下(如果您已经知道它的作用,请跳过以下块):

异常处理应用程序块旨在集中并使用配置文件完全可配置以下关键异常处理任务

  • 记录异常
  • 替换异常
  • 包装异常
  • 传播异常
  • 等等

该库通过让您修改 try catch 块来做到这一点,如下所示:

try
{
  // Run code.
}
catch(DataAccessException ex)
{
    bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Policy");
    if (rethrow)
    {
        throw;
    }
}

根据 app.config 中为策略名称指定的内容(请参阅此处的文档),HandleException 将...

  • 抛出一个全新的异常(替换原来的异常)
  • 将原始异常包装在一个新异常中并抛出该异常
  • 吞下异常(即什么都不做)
  • 你有没有重新抛出原来的异常

此外,您还可以预先对其进行配置以执行更多操作(例如记录异常)。

现在这是我的问题:我完全看不出无论是替换、包装、吞下还是重新抛出异常,使其可配置有什么好处。根据我的经验,这个决定必须在您编写代码时做出,因为当您更改异常处理行为时,您通常必须更改周围或调用代码。

例如,当您重新配置时,您的代码可能会开始出现不正确的行为,使得在特定点抛出的特定异常现在被吞没而不是重新抛出(catch 块之后可能有代码在发生异常时不得执行)。异常处理中所有其他可能的更改也是如此(例如,替换 -> 重新抛出,吞下 -> 包装)。

所以,对我来说,最重要的是异常处理块解决了实际上不存在的问题。异常记录和通知位很好,但不是所有其他的东西只是过度工程的完美例子吗?

4

6 回答 6

13

如果您将异常用于控制流,那么您将希望远离基于策略的异常处理。

但是对于您希望将其视为不可恢复的异常(后台任务失败、套接字断开连接、文件已删除等),您可能希望进行可配置的、基于策略的异常处理。

例如,如果您正在开发 API,您可能希望 API 中的每个函数只抛出标准异常(ArgumentException等),以及在内部非标准异常的情况下,您自己的库特定异常(例如一个MyLibraryException)。在这种情况下,重要的是某些东西不能正常工作。您没有挑选异常并找出问题所在。你只是承认有些事情出了问题,你现在应该做一些事情。

有些东西应该是可配置的,因为你做什么并不重要。向用户显示消息框?模态还是非模态?记录异常?你想如何记录异常?调用日志记录 Web 服务?追加到日志文件?写入 Windows 事件日志?在数据库中插入一个条目?在两个数据库中插入一个条目?这对您的应用程序的其余部分并不重要。如何处理异常的选择与应用程序的其余部分完全正交。

(顺便说一句,这不是我处理可配置的基于策略的异常处理的方式。我更倾向于 AOP 风格,例如在容器中注册异常记录器拦截器。)

于 2009-01-03T02:32:25.377 回答
5

我在开发没有可恢复状态的函数时遇到了这个问题。我相信这种策略驱动的异常处理实际上是有用的,并确保作为该项目一部分的所有其他开发人员实际上都遵守不可恢复异常的标准。

我同意上面的海报,如果您将它们用于控制流,您可能希望远离基于策略的异常。

于 2009-01-03T03:24:18.097 回答
4

我不得不同意“异常处理块解决了实际上不存在的问题”的说法。自从它发布以来,我一直在使用它,我很少觉得我实际上通过使用它获得了很多。好吧,也许有点头疼。:)

在开始之前,我承认我确实喜欢WCF 服务边界处的异常屏蔽功能。然而,这是最近才添加的,不涉及编码——只涉及属性和配置,而不是通常出售块的方式。这是微软在http://msdn.microsoft.com/en-us/library/cc309250.aspx上展示的方式

替代文字

在我看来,以上是流量控制。

try
{
  // Run code.
}
catch(DataAccessException ex)
{
    bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Policy");
    if (rethrow)
    {
        throw;
    }
}

当您将流控制方面与上面的代码结合起来时,您肯定不会让错误的代码看起来不正确。(不要介意 if (rethrow) 构造并没有提供太多抽象。)对于不熟悉策略定义的开发人员来说,这会使维护变得更加困难。如果postHandlingAction配置文件中的 更改(它可能会在某个时候发生),您可能会发现应用程序以从未测试过的意想不到的方式运行。

如果该块不处理流控制,而只是允许您将处理程序链接在一起,也许我不会觉得它令人反感。

但也许不是。我实际上不记得曾经被要求:

  • 在处理异常时添加一项新功能(例如,除了记录到事件日志之外,还可以发送电子邮件。实际上,如果您自己已经记录了异常,则记录块可以自行完成。:))
  • 在整个“策略”中更改异常处理行为(吞下、重新抛出或抛出新的)。

我并不是说它不会发生(我敢肯定有人有故事!);我只是觉得异常处理应用程序块使程序更难理解和维护,这通常超过了块提供的功能。

于 2010-01-21T08:36:02.650 回答
2

我认为这根本不是过度工程。实际上。异常可以通过中央处理程序这一事实在我的工作中是一件好事。我有开发人员会吃掉每一个异常 - 处理或不处理,这样它就不会冒泡到顶部并在最终用户面前提出一些令人担忧的事情,或者在未被捕获时严重搞砸服务/守护进程。

现在有了这个策略,我们可以随时开始记录,而无需重新启动或在整个应用程序中不必要地添加记录逻辑。现在我们可以在不让应用离线的情况下查看异常等。

如果你问我,智能编程......

于 2009-01-03T03:38:07.273 回答
1

很简单:如果您希望发布代码中的异常处理与调试代码中的不同,那么它会很有用。

于 2009-01-03T02:16:42.537 回答
0

在我看来,使用异常处理块的一个真正价值始于你的 catch 块中的一行代码:

bool rethrow = ExceptionPolicy.HandleException(例如,“数据访问策略”);

一行代码——你能做到多简单?在一行代码的背后,策略,因为它的配置,使得对策略的分配/更新很容易执行,所有这些都不需要重新编译源代码。

如前所述,异常策略实际上可以执行许多操作——无论您的策略中定义了什么。隐藏在 catch 块中显示的一行代码背后的复杂性是我看到真正价值的地方。

我会说复杂,但不是过度设计。所以我的想法是,当您需要通过灵活地轻松更改配置来改变处理/记录异常的方式时,在维护期间将会看到巨大的红利,并且仍然拥有相同的一行源代码。

于 2009-01-03T03:04:33.643 回答