Try Catch Finally使用.NET4.5.1阻止问题



我有一个简单的try-catch finally代码块,它在.NET3.5中正常工作,但在使用.NET4.5.1创建的项目中,相同的代码表现完全不同。基本上,在.NET4.5.1中,如果发生异常,"finally"块不会被命中,这不是我从try-catch finally块中预期的行为。我尝试了不同的机器,我的另外两个同事也在尝试,我们都得到了相同的结果。这是我关心的问题,因为我使用finally块来关闭DataReaders、某些连接等等。

如果在没有调试器的情况下以RELEASE模式引发异常,或者在运行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在出现未处理的异常时结束程序。如果处理了异常,运行finally块只需要。在出现未处理的异常时,CLR是否被要求、允许或不允许执行finally块的问题上,规范是模糊的;安全的假设是,这是规范未定义的行为,取决于特定的实现。

CLR可以随意选择为未处理的异常运行finally块,也可以不运行。许多人认为CLR使用了这种算法:在出现异常时,向上遍历调用堆栈,在执行过程中执行finally块,寻找处理程序;如果找不到处理程序,请终止进程。在具有未处理异常的程序中,CLR不需要符合此算法。特别是,CLR被允许通过黑魔法来确定没有异常处理程序,并且从不运行任何finally块。在某些情况下,它是否选择在CLR的某些版本中这样做,我不知道。在任何情况下,您都不能依靠该行为来确保程序的正确性,因为具有未处理异常的程序是不正确的

该规范还指出,CLR可以随意选择是否提供启动调试器。CLR在调试或发布中不需要做同样的事情,也不需要在不同版本之间做同样的事。

这里的问题是,你根据过去的经验形成了一种期望,但没有文件表明过去的经验是预测未来的基础。恰恰相反;CLR被允许在具有未处理异常的程序中根据月相改变其行为(如果它愿意的话)。

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

如果我理解正确的话,只要上游有另一个catch,finally块就会执行吗?

不,我没这么说。让我们把它分解一下。

如果程序中存在未捕获的异常,则程序的行为是由实现定义的。无论你得到什么行为,那就是你得到的行为,CLR都有权产生这种行为。这包括运行finally块和不运行finally区块。

假设没有未捕获的异常,并且抛出了一个异常,并且在捕获的过程中有一个finally块。是否保证finally块将执行。有很多事情可以阻止最终阻止在合法程序中执行。例如,另一个finally块或异常过滤器可能会进入无限循环或快速失败,这两种情况都会阻止finally块的执行。如果你绝对肯定必须运行一些清理代码,那么你需要研究受约束的执行区域。(我不知道它们是如何工作的;我从来没有必要学习。我听说它们很棘手。)。

可以保证的是,如果控件离开一个最终受保护的块,那么finally代码将运行。异常筛选器期间运行的代码不算作离开块,快速失败不会导致程序控制退出块,而是导致程序控制突然结束。显然,无限循环导致控制永远不会退出块。

我想,在一个真正未处理的异常的情况下,程序无论如何都应该终止,这样孤立的DB连接/事务就不会成为问题吗?

这是否是一个问题,我不能说。询问数据库的作者。

程序很可能会终止,不过我再次注意到CLR不需要有这种行为。例如,假设某个线程在CLR试图确定是否安装了调试器时仍在运行。CLR有权花任意长的时间来解决这个问题,因此也有权保持线程运行。不管是不是,我不知道。我所知道的是,我不想依赖任何一种行为。

此外,使用"AppDomain.CurrentDomain.UnhandledException"事件计数是否为"正在处理">

没有。如果该程序运行,则存在未处理的异常,并且程序的行为是由实现定义的。该事件处理程序应该只用于记录程序存在错误的事实。

除了Lipper写的内容外,请注意它是在MSDN中写的。。。在尝试之下。。。最后:

但是,如果未处理异常,finally块的执行取决于如何触发异常展开操作。这反过来又取决于你的电脑是如何设置的。

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

,然后它继续解释,如果将try... catch置于"高"级别,则内部try... finally将被执行。

在Framework 4.0之前,未处理的异常启动了"Microsoft.NET错误报告垫片",它显示了提供"调试"或"关闭程序"的对话框。该填充程序允许.NET应用程序"干净"地关闭。

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