Try-Catch-Finally阻止了.NET4.5.1的问题

我有一个简单的try-catch-finally代码块,它在.NET3.5中按预期工作,但在使用.NET4.5.1创建的项目中,相同的代码表现完全不同。 基本上,在.NET4.5.1中,如果发生exception而不是我在try-catch-finally块中预期的行为,则“finally”块不会被命中。 我在不同的机器上试过,我的另外两个同事也在尝试,我们都得到了相同的结果。 这对我来说很关心,因为我使用finally块来关闭DataReader,某些连接以及诸如此类的东西。

如果在没有调试器的RELEASE模式下抛出exception或运行RELEASE编译的EXE文件,.NET4.5.1不会触发“finally”块。 在调试模式下,两个.NET版本都命中了“finally”块。

同样,下面的代码在.NET3.5 RELEASE模式下没有调试器但在.NET4.5.1中没有表现。 我错过了什么吗? 有人可以帮忙吗?

class Program { static void Main(string[] args) { try { string a = null; var x = a.Length; Console.WriteLine(x); } catch (Exception ex) { throw; } finally { Console.WriteLine("This is the finally block."); } Console.WriteLine("You should not be here if an exception occured!"); } } 

下面的代码在.NET3.5 RELEASE模式下没有调试器但在.NET4.5.1中没有表现。 我错过了什么吗?

注意:我夸大了这种行为的未定义水平; 感谢评论员Voo指出这一点。 我应该首先回到规范。

是。 CLI规范要求CLR在存在未处理的exception时结束程序。 如果处理exception,则只需要运行finally块。 对于在存在未处理的exception时是否需要,允许或不允许执行最终块的问题,规范是模糊的; 然后安全的假设是说这是规范未定义的行为,这取决于特定的实现。

CLR可以随心所欲地选择是否运行最终块来处理未处理的exception。 许多人认为CLR使用这种算法:在exception时,走向调用堆栈,随时执行finally块,寻找处理程序; 如果未找到处理程序,则终止该进程。 CLR不需要在具有未处理exception的程序中符合此算法。 特别是,允许​​CLR通过黑魔法确定没有exception处理程序,并且永远不会运行任何finally块。 在某些情况下,无论是否选择在CLR的某些版本中这样做,我都不知道。 在任何情况下,您都不能依赖该行为来确定程序的正确性,因为具有未处理exception的程序不正确。

该规范还指出,CLR可以随心所欲地选择是否启动调试器。 CLR不需要在调试或发布中执行相同的操作,并且不需要在版本之间执行相同的操作。

这里的问题是你根据过去的经验形成了一个期望,但没有文件说过去的经验是预测未来的基础。 相反,恰恰相反; 如果喜欢CLR,则允许CLR在具有未处理exception的程序中根据月亮的相位改变其行为。

如果您希望程序行为可预测,则不要抛出未处理的exception

所以如果我理解正确的话,只要在上游某处有另一个捕获,finally块就会执行?

不,我没有这么说。 让我们分解吧。

如果程序中存在未捕获的exception,则程序的行为是实现定义的。 无论你得到什么行为,这都是你得到的行为,而CLR是在产生这种行为的权利范围内。 这包括运行finally块和不运行finally块。

假设没有未捕获的exception,并抛出exception,并且在捕获的过程中有一个finally块。 是否保证finally块将执行? 。 有许多事情可以阻止最终阻止在法律程序中执行。 例如,沿途的另一个最终块或exceptionfilter可能会进入无限循环或快速失败,其中任何一个都会阻止finally块执行。 如果您绝对必须运行一些清理代码,那么您需要研究约束执行区域。 (我不知道他们是如何工作的;我从来没有必要学习。我听说他们很棘手。)

保证的是, 如果控制离开最终受保护的块,则最终代码将运行 。 在exceptionfilter期间运行的代码不计入离开块,并且快速失败不会导致程序控制退出块,这会导致程序控制突然结束。 显然,无限循环会导致控制从不退出块。

我想在一个真正未处理的exception的情况下,程序应该终止,所以孤立的数据库连接/事务应该不是问题?

无论是否是一个问题,我都不能说。 询问您的数据库的作者。

该程序很可能会终止,但我再次注意到CLR不需要具有该行为。 例如,假设有一些线程在CLR试图判断您是否安装了调试器时继续运行。 CLR在权利范围内可以任意长时间地解决这个问题,因此在保持该线程运行的权利范围内。 无论是否,我不知道。 我所知道的是,我不想依赖任何一种行为。

另外,使用’AppDomain.CurrentDomain.UnhandledException事件计数作为’处理’

不。 如果那个东西运行然后有一个未处理的exception,程序的行为是实现定义的。 该事件处理程序应仅用于记录程序有错误的事情。

在Lipper编写的内容之上,请注意它是用MSDN编写的……在try …最后 :

但是,如果未处理exception,则finally块的执行取决于如何触发exception展开操作。 反过来,这取决于您的计算机的设置方式。

通常,当未处理的exception结束应用程序时,无论finally块是否运行都不重要。

然后它继续解释,如果你try... catch “高”水平,那么内部try... finally将被执行。

在Framework 4.0之前,未处理的exception启动了“Microsoft .NET Error Reporting Shim”,它显示了提供“Debug”或“Close program”的对话框。 垫片允许.NET应用程序“干净地”关闭。

从Framework 4.0开始(据我所知)未处理的exception导致Windows启动Windows错误报告(WER),在任务管理器中显示为Windows问题报告。 该应用程序显示了与填充程序类似的对话框,但采用了更强硬的方法来杀死应用程序,可能调用TerminateProcess或TerminateThread,这将不允许任何进一步的代码在行为不当的过程中执行。