.NET和C#exception。 抓住什么是合理的

免责声明,我来自Java背景。 我没有做太多的C#。 这两个世界之间有很多转移,但当然存在差异,其中一个是exception倾向于被考虑的方式。

我最近回答了一个C#问题,建议在某些情况下,这样做是合理的:

try { some work } catch (Exeption e) { commonExceptionHandler(); } 

(原因无关紧要)。 我收到的答复我不太明白:

直到.NET 4.0,捕获exception非常糟糕。 这意味着你可以捕获各种低级致命错误,从而掩盖错误。 这也意味着如果某种类型的损坏触发了这样的exception,堆栈上的任何打开的finally块都将被执行,所以即使callExceptionReporter函数尝试记录并退出,它甚至可能无法到达那一点( finally块可能会再次抛出,或导致更多损坏,或从磁盘或数据库中删除重要的东西)。

我可能比我意识到的更困惑,但我不同意其中的一些。 请其他人评论。

  1. 我知道我们不想接受许多低级exception。 我的commonExceptionHandler()函数可以合理地重新抛出那些。 这似乎与对相关问题的答案一致。 这说“根据你的上下文,使用catch(…)是可以接受的,只要重新抛出exception即可。” 所以我总结使用catch(Exception)并不总是邪恶,默默地吞下某些例外。

  2. 短语“直到.NET 4对Catch Exception来说非常糟糕”.NET 4中有哪些变化? 这是对AggregateException的引用,它可能会给我们一些与我们捕获的exception有关的新事物,但我不认为更改是基本的“不要吞下”规则。

  3. 下一句话真的很麻烦。 这可能是对的吗?

它还意味着如果某种类型的损坏触发了这样的exception,堆栈上的任何打开的finally块都将被执行(finally块可能再次抛出,或导致更多损坏,或从磁盘或数据库中删除重要的东西)

我的理解是,如果有一些低级代码

 lowLevelMethod() { try { lowestLevelMethod(); } finally { some really important stuff } } 

在我的代码中我调用lowLevel();

  try { lowLevel() } catch (Exception e) { exception handling and maybe rethrowing } 

无论我是否捕获exception,这对于finally块的执行都没有任何影响。 当我们离开lowLevelMethod()时,finally已经运行了。 如果最终要做任何坏事,比如我的磁盘损坏,那么它会这样做。 我捕捉exception没有任何区别。 如果它到达我的exception块我需要做正确的事情,但我不能成为dmis执行最终的原因

我的座右铭是处理您可以(或需要)的内容,并让任何其他exception冒泡并在UnhandledException事件中捕获它们。

你是正确的,在退出方法之前总是调用finally块(即使在try部分引发exception的情况下)。 所以你是否想要在out方法上捕获exception完全取决于你……这不应该影响被调用的finally块。

对于问题#2:作者的意思是“腐败的国家例外”。 它们将在.NET 4.0中引入(CLR团队在此次演讲中在PDC 2008上宣布了这一点 )。

正常的catch块无法捕获损坏的状态exception。 示例:访问冲突,无效内存。

但您可能想要捕获这些exception:

  1. 在main()中 – 写入日志,退出,关闭插件
  2. 非常罕见的情况,当你知道代码抛出这样的exception时(例如某些与本机代码互操作的情况)

为此,您应该将属性[HandleProcessCorruptedStateException]放在要捕获CorruptedStateException的方法中。

要阅读有关这些exception的更多信息,请参阅此 MSDN文章。

作为一般规则,您不应该捕获exception,除非:

  1. 您有一个特定的例外,您可以处理和执行某些操作。 但是,在这种情况下,您应该始终检查您是否应该首先考虑并避免例外。

  2. 您位于应用程序的顶层(例如UI),并且不希望将默认行为呈现给用户。 例如,您可能需要一个带有“请发送给我们您的日志”样式消息的错误对话框。

  3. 在以某种方式处理exception后重新抛出exception,例如,如果您回滚数据库事务。

你的finally块总是执行。 你的代码示例是正确的 – 低级方法应该try finally 。 例如,非托管调用可能知道它需要处理非托管资源,但这不会暴露给调用它的.Net方法。 该调用应该摆脱finally块中的非托管资源,并且调用托管方法可以处理exception或者只是将它们传递给它们。

如果您需要处理exception中的某些内容,则应重新抛出exception,例如:

 try { conn.BeginTransaction(); //do stuff conn.CommitTransaction(); } catch (Exception) { conn.RollbackTransaction(); //required action on any exception throw; //re-throw, don't wrap a new ex to keep the stack trace } finally { conn.Dispose(); //always dispose of the resource } 

仅针对您的第三个问题:

如果你有

 nastyLowLevel() { doSomethingWhichMayCorruptSomethingAndThrowsException(); } MyEvilCatcher() { try { nastyLowLevel(); } catch (Exception e) { MyExceptionHandler(e); } } WiseCatcher() { try { MyEvilCatcher(); } catch (LowLevelException e) { DoSomethingWiseSoFinnalyDontRuinAnything(); } finally { DoSomethingWhichAssumesLowLevelWentOk(); } } 

我认为你提出的回答只是意味着一些低级方法可以使用exception来告知某些外部方法必须特别小心。 如果您在catch-all-handler中忘记了这些exception并且不重新抛出它们,那么可能会出现问题。

一般来说,我更愿意仔细考虑可能会抛出哪些exception并明确地捕获它们。 我只在生产环境的外层使用“通用处理程序”,以便记录意外的exception并以格式良好的方式将它们呈现给客户(包括向我们发送日志文件的提示。)

我认为引用的响应是错误的(或者可能意味着2.0而不是4.0)? 这对我来说听起来有点虚伪。