如果我退出C#中的try / finally块,那么finally中的代码总会运行吗?

时间:2012-04-21 02:54:19

标签: c# try-catch-finally

看起来它确实按照一些初步测试,但我想知道的是,如果保证返回,或者在某些情况下它无法返回?这对我的应用程序至关重要,但我还没有找到一个用例,但它不会返回。
我想获得有关这一主题的专业知识。

4 个答案:

答案 0 :(得分:34)

其他答案中有许多不准确之处。

当控件离开try块正常时,控制权被传递给finally块 - 也就是说,通过返回,转到,中断,继续或简单地从结束落下来。当控制通过一个被封闭的catch块捕获的异常离开try块时,控制被传递给finally块。

在所有其他情况下,没有保证将调用finally块中的代码。特别是:

  • 如果try块代码进入无限循环,或者线程被冻结并且从未解冻,则永远不会调用finally块代码。

  • 如果进程在调试器中暂停然后被激活地终止,则从不调用finally块。如果进程执行失败快速,则永远不会调用finally块。

  • 如果将电源线拉出墙壁,则永远不会调用finally块。

  • 如果抛出但没有相应的catch块的异常,那么finally块是否运行是运行时的实现细节。当存在未捕获的异常时,运行时可以选择任何行为。 “不运行finally块”和“运行finally块”都是“任何行为”的示例,因此可以选择其中之一。通常,运行时所做的是询问用户是否要在finally块运行之前附加调试器;如果用户说不,则finally块运行。但同样:运行时不是必需来做到这一点。它可能会快速失败。

你不能依赖于始终被调用的finally块。如果你需要强有力的保证代码执行,那么你不应该写一个try-finally,你应该写一个constrained execution region。正确编写CER是C#编程中最困难的任务之一,因此在尝试编写代码之前请仔细研究文档。

顺便提一下,关于最终被封锁的一个“有趣的事实”是:

try { goto X; } finally { throw y; } 
X : Console.WriteLine("X");

X是一个无法访问的标签,目标是可访问的goto!所以,下次你参加派对时,你可能会像“大家一样,任何人都可以制作一个C#程序,其中有一个无法访问的标签,目标是可访问的goto?”你会看到派对上谁已经阅读了可达性规范,谁没有阅读!

答案 1 :(得分:12)

无论try或catch块内发生了什么,finally块中的任何内容都将始终执行。如果你从方法中返回它并不重要。

唯一的例外是,如果finally块中的代码抛出异常,那么它将像任何其他代码块一样停止执行。

关于goto,答案仍然是肯定的。请考虑以下代码:

try
{
    Console.WriteLine("Inside the Try");
    goto MyLabel;
}
finally
{
    Console.WriteLine("Inside the Finally");
}

MyLabel:
    Console.WriteLine("After the Label");

产生的输出是:

  

尝试内部

     

在最后

中      

标签后

答案 2 :(得分:6)

以下是一些例子:

<强> Environment.FailFast()

        try
        {
            Console.WriteLine("Try");
            Environment.FailFast("Test Fail");

        }
        catch (Exception)
        {
            Console.WriteLine("catch");
        }
        finally
        {
            Console.WriteLine("finally");
        }

输出只是“尝试”

<强>#1

        try
        {
            Console.WriteLine("Try");
            Rec();
        }
        catch (Exception)
        {
            Console.WriteLine("catch");
        }
        finally
        {
            Console.WriteLine("finally");
        }

Rec在哪里:

    private static void Rec()
    {
        Rec();
    }

输出仅为“Try”,并且由于StackOverflow,该过程终止。

无歧义的例外

        try
        {
            Console.WriteLine("Try");
            throw new Exception();
        }
        finally
        {
            Console.WriteLine("finally");
        }

答案 3 :(得分:1)

如果发生终止应用程序的致命异常,则不会调用最后一个块。包括堆栈溢出,在调用方法的JIT期间出现异常,致命异常包括CLR运行时。

正如@mintech指出,如果应用程序挂在块中,它根本不会到达finally块。这包括等待同步对象,死锁无限循环甚至是无法关闭它的UI。