Try-Catch-Finally阻止.NET4.5.1

时间:2015-06-16 22:47:27

标签: c# exception

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

.NET4.5.1没有点击"最后"如果在没有调试器的 RELEASE 模式下或在运行 RELEASE编译的EXE 文件时抛出异常,则阻止。在调试模式下,两个.NET版本最终都击中了#34;块。

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

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!");
    }
}

3 个答案:

答案 0 :(得分:19)

  

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

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

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

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

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

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

如果您希望程序的行为可预测,那么不会抛出未处理的异常

  

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

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

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

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

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

  

我认为在真正未处理的异常的情况下,程序应该终止,因此孤立的数据库连接/事务不应该成为问题?

不管是不是问题,我不能说。询问您的数据库的作者。

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

  

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

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

答案 1 :(得分:4)

除了理柏所写的内容之外,请注意它是用MSDN编写的......在try...finally下:

  

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

  

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

然后继续解释如果你将try... catch置于“高”级别,那么内部try... finally将被执行。

答案 2 :(得分:3)

在Framework 4.0之前启动了未处理的异常' Microsoft .NET Error Reporting Shim',它显示了提供给' Debug'或者'关闭程序'。垫片允许.NET应用程序干净地关闭"。

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