【问题标题】:Exception handling practices异常处理实践
【发布时间】:2008-10-13 13:35:15
【问题描述】:

无论如何,我对何时传播异常以及何时包装它以及它们之间的区别有点困惑。

目前,我的理解告诉我,包装异常将涉及获取像 DriveNotFound(在 IO 中)这样的异常,然后用一般 IOException 包装它。

但是对于传播异常的概念,这是否只有在我有一个空的 catch 子句时才会发生?因此,在 ASP.NET Web 应用程序中,它会传播到 global.asax。或者在最近部署的 Web 应用程序的情况下,未处理的 HTTPException 导致黄屏死机并将日志写入 Windows Server(这是我正在重写的 Web 应用程序)。所以异常发生在方法中,可以在类级别处理,显示在页面中,然后上到 global.asax 或 Windows Server。

为什么我想用更通用的异常来包装异常?规则是处理具有最特定类型的异常(因此 DriveNotFound 显然是未找到驱动器)。另外,我将如何在包装和替换异常之间进行选择?

异常处理链只是 try 和 catch(或 catches)子句吗?我从措辞上假设是的。

最后,为什么以及如何让异常向上传播到调用堆栈?

我确实阅读了有关异常处理的 MS PandP 指南,但我想这些示例并没有让我充分理解所有内容。

这个问题来自企业库包装/传播异常等的能力。这是我不确定的传播,以及替换/包装异常的差异。

另外,是否可以在 catch 块中插入复杂的错误处理逻辑(例如 ifs/elses 和类似的东西)。

谢谢

【问题讨论】:

    标签: c# exception


    【解决方案1】:

    不少于 6 个问题 :-)

    但是对于传播异常的概念,这是否只有在我有一个空的 catch 子句时才会发生?

    一个异常将向上传播,直到它被调用堆栈中更上层的 catch 块捕获,该调用堆栈处理该特定异常的类型或更接近该异常层次结构的基类型的异常类型。因此,例如,所有托管异常都派生自 System.Exception,因此拦截 System.Exception 的 catch 块将捕获每个托管异常。

    为什么我想用更通用的异常来包装异常?

    我不确定您所说的“包装”是什么意思。您的意思是捕获一个异常,将其替换为另一个异常,然后将原始异常添加为新异常的 InnerException 属性?还是别的什么?

    我认为很少有充分的理由将异常替换为更通用的异常。但您当然可以将一个异常替换为另一个异常,原因有 3 个中的一个或多个:

    • 向调用者隐藏实现细节。
    • 提高抽象级别,使其对调用者更有意义。
    • 抛出一个针对当前问题的自定义异常。

    另外,我将如何在包装和替换异常之间进行选择?

    抱歉,我还是不明白你是如何将这两者定义为不同的。

    异常处理链只是 try 和 catch(或 catches)子句吗?

    以下是引发异常时发生的基本情况:

    • CLR 依次遍历本地 Try...End Try 块中的 Catch 块列表,寻找具有与引发的异常匹配的异常过滤器的本地 Catch 块。

      李>
    • 如果本地 Catch 块具有与引发的确切异常匹配的异常过滤器,则执行该 Catch 块中的代码,然后执行 finally 块中的代码。然后在 End Try 之后的第一条语句处继续执行。

    • 或者,如果引发的异常源自本地 Catch 块指定的异常,则会发生与第二步中所述相同的操作。例如,捕获 ArgumentException 的异常过滤器也会捕获从 ArgumentException 派生的异常,例如 ArgumentNullException、InvalidEnumArgumentException、DuplicateWaitObjectException 和 ArgumentOutOfRangeException。

    • 如果没有本地 Catch 块与引发的异常匹配,CLR 会逐个方法返回调用堆栈,寻找想要响应异常的 Catch 块。如果在调用堆栈中没有找到匹配的 Catch 块,则认为该异常未处理。

    • 或者,如果在调用堆栈的某处找到匹配的 Catch 块,则会执行 throw 和 catch 之间的每个 finally 块中的代码。这从属于抛出异常的 Try 块的 finally 开始,并以在捕获异常的方法下面的方法中的 finally 结束。

    • 在对以下所有捕获异常的方法完成清理后,控制权将转移到捕获异常的 Catch 块,并执行此代码。接下来要运行的是 Try 的 finally 块,在该块中捕获了异常。现在调用堆栈已经展开并且错误清理已经完成,最后一步是在捕获异常的 End Try 之后的第一条语句处继续执行。

    • 如果 Catch 块中的代码导致引发另一个异常,则使用 InnerException 属性将原始异常自动附加到新异常。通过这种方式,可以堆叠异常而不会丢失任何信息。

    • 您应该避免将清理代码放在可能引发异常的 finally 块中,除非该代码位于其自己的 Try 块中。如果没有这种额外的保护,CLR 的行为就好像新异常是在 finally 块之后的 end 之后抛出的,并在调用堆栈中查找想要响应新异常的远程 Catch 块。除非原始的 Catch 块保存它,否则原始异常将丢失。

    最后,为什么以及如何让异常向上传播到调用堆栈?

    为什么:当你不具体理解异常并知道如何从中恢复时,你应该让它向上传播。

    如何:仅捕获您了解并知道如何处理的异常类型。有时您需要任何异常的详细信息才能进行适当的恢复。在这种情况下,您可以捕获它,进行恢复,然后使用 throw; 语句重新抛出它。

    另外,是否可以在 catch 块中插入复杂的错误处理逻辑(例如 ifs/elses 和类似的东西)。

    通常是的,因为由 Catch 块中的代码引起的任何新异常都会通过 InnerException 属性自动附加旧异常。但是如果你能避免它,那么激发这个机制是不明智的,所以你拥有的代码越简单越好。保持 Catch 代码简单的另一个好理由是,它通常不会像您的主线代码那样经过相同程度的测试。

    【讨论】:

      【解决方案2】:

      已经有一个很好的问题,有很多很好的答案和讨论。见这里:

      Best Practice for Exception Handling in a Windows Forms Application?

      【讨论】:

        【解决方案3】:

        这一切都是为了向您的来电者传达正确的含义。我怀疑是否有理由将特定异常包装在更通用的异常中 - 这对任何人都没有帮助。

        考虑一个与文件访问无关但在后台访问配置文件的 API。如果配置文件不存在,您可以将FileNotFoundException 包装在ConfigurationException 中,以便将正确的问题传达给调用者。

        我为什么以及如何让 异常向上传播调用堆栈?

        如果你不能处理它,你会让异常传播。就是这么简单。如果您的代码无法或应该做任何事情来解决异常,那么让它传播。传播时,请注意如何进行:

        throw ex;
        

        不同于:

        throw;
        

        前者丢弃旧的堆栈跟踪并从引发异常的点创建另一个。后者保留原始堆栈跟踪。

        当然,如果您无法处理异常,那么您可能一开始就不会费心去捕捉它(也许您想记录一下)。

        【讨论】:

          【解决方案4】:

          我通常这样做:

          • 除非完全理解,否则业务逻辑程序集 (dll) 不会处理异常
          • 预期但无法处理的异常被包装在单个异常类型中,并允许汇总调用堆栈(即,在数据库交互期间可能发生的所有不同异常都被包装在单个 RepositoryException 中并抛出)
          • 允许传播意外异常(切勿在 dll 中包含 catch(Exception ex))
          • 仅在可能的最后时刻(即 UI 控制器)处理异常

          通常只有在调用堆栈的最上层才能确切知道自己在做什么(当前的业务流程是什么)以及如何正确处理异常。

          【讨论】:

            【解决方案5】:

            这是我发现的在 .NET 中实现连贯异常处理策略的最佳资源

            http://msdn.microsoft.com/en-us/library/cc511522.aspx

            【讨论】:

              猜你喜欢
              • 2018-01-03
              • 1970-01-01
              • 1970-01-01
              • 2017-05-11
              • 2013-05-09
              • 2011-11-10
              • 1970-01-01
              • 2013-04-22
              相关资源
              最近更新 更多