c# - 异常处理实践

标签 c# exception

无论如何,我对何时传播异常以及何时包装异常以及差异感到有些困惑。

目前,我的理解告诉我,包装异常将涉及采取像 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 和类似的东西)。

谢谢

最佳答案

不少于 6 个问题:-)

But with the concept of propagating an exception, is this only something that happens if I have an empty catch clause?



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

Why exactly do I want to wrap an exception with a more generic one?



我不确定你所说的“包装”是什么意思。你的意思是捕获一个异常,用另一个替换它,然后将原来的添加为新异常的 InnerException 属性?或者是其他东西?

我认为很少有充分的理由用更通用的异常替换异常。但是您当然可以用另一个异常替换一个异常,原因有一个或多个:
  • 向调用者隐藏实现细节。
  • 提高抽象级别,使其对调用者更有意义。
  • 抛出一个非常特定于手头问题的自定义异常。

  • Also, how would I choose between wrapping and replacing an exception?



    对不起,我还是不明白你是如何定义这两个不同的。

    Is the exception handling chain just the try and catch (or catches) clauses?



    以下是抛出异常时发生的基本情况:
  • 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 块之后的结束之后抛出的,并在调用堆栈中查找想要响应新异常的远程 Catch 块。除非原始 Catch 块保存它,否则原始异常将丢失。

  • Finally, why and how would I want to let an exception propagate up the callstack?



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

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

    Also, is it ok to insert complex error handling logic in a catch block (e.g. ifs/elses and things like that).



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

    关于c# - 异常处理实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/197593/

    相关文章:

    c# - Outlook VSTO 功能区到主页 tabControlId

    c# - 在结果中包含正面前瞻中的字符

    c# - 无法用 lambda 表达式替换字符串 propertyName 参数

    c# - 方法 'tostring' 没有重载需要 1 个参数

    Java:如何在插入异常时传播 "method exit"?

    android - 如何使用 Crittercism 手动发送异常?

    c# - 删除另一个进程正在使用的文件

    objective-c - UIPopoverPresentationController 应该在 iOS 9 上呈现之前设置一个非零的 sourceView 或 barButtonItem

    c++ - 结构化异常处理程序 (SEH) 不会捕获堆损坏

    java - Apache Hadoop setXIncludeAware UnsupportedOperationException