43

它似乎按照一些初始测试进行,但我想知道它是否可以保证返回,或者在某些情况下它是否不能返回?这对我的应用程序至关重要,但我还没有找到不会返回的用例。
我想获得这方面的专业知识。

4

4 回答 4

72

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

当控制正常离开 try 块时,控制权被传递给 finally 块——也就是说,通过 return、goto、break、continue 或简单地从 end 上掉下来。当控制通过已被封闭的 catch 块捕获的异常离开 try 块时,控制将传递给 finally块。

在其他所有情况下,都不能保证finally 块中的代码会被调用。尤其:

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

  • 如果进程在调试器中暂停,然后主动终止,那么 finally 块永远不会被调用。如果进程执行了快速失败,则永远不会调用 finally 块。

  • 如果电源线从墙上拔出,则永远不会调用 finally 块。

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

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

顺便说一句,关于最终被阻止的 goto 的“有趣事实”是:

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

X 是一个不可访问的标签,由可访问的 goto 定位!因此,下次你参加聚会时,你可能会说“大家好,任何人都可以制作一个 C# 程序,它有一个不可访问的标签,并且是一个可访问的 goto 的目标吗?” 你会看到聚会上谁阅读了可达性规范,谁没有!

于 2012-04-21T15:18:18.390 回答
51

在正常情况下,无论 try 或 catch 块内发生什么,都会执行 finally 块中的代码。您是否从该方法返回都没有关系。

在某些情况下,这是不正确的。例如,如果 finally 块中的代码抛出异常,那么它将像任何其他代码块一样停止执行。

Eric Lippert 写了一个更全面的答案,概述了其他案例:https ://stackoverflow.com/a/10260233/53777

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

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

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

产生的输出是这样的:

在尝试里面

最后里面

标签之后

于 2012-04-21T02:56:13.403 回答
7

这里有些例子:

Environment.FailFast()

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

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

输出只是“尝试”

堆栈溢出

        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");
        }
于 2012-04-21T15:50:34.257 回答
1

在终止应用程序的致命异常的情况下,将不会调用 finally 块。包括堆栈溢出、JIT 调用方法期间的异常、CLR 运行时中的致命异常。

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

于 2012-04-21T03:06:01.707 回答