如果我在 C# 中返回 try/finally 块,最终的代码是否总是运行

本文关键字:代码 是否 运行 返回 finally try 如果 | 更新日期: 2023-09-27 18:31:54

根据

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

如果我在 C# 中返回 try/finally 块,最终的代码是否总是运行

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

当控制正常离开 try 块时,控制权将传递给 finally 块 - 即通过返回、转到、中断、继续或只是从末尾掉下来。当控制通过封闭的 catch 块捕获的异常离开 try 块时,控制权将传递给 finally 块。

在所有其他情况下,不能保证最终块中的代码将被调用。特别:

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

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

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

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

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

顺便说一句,关于最终被阻止的gotos的一个"有趣的事实"是:

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

X 是可访问的 goto 定位的无法访问的标签! 因此,下次你参加聚会时,你可以像"嘿大家好,任何人都可以制作一个C#程序,该程序具有可访问的标签,而该标签是可访问的goto的目标吗?"你会看到聚会上谁已经阅读了可访问性规范,谁没有!

在正常情况下,无论 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");

生成的输出是这样的:

尝试内部

里面终于

标签后

以下是一些示例:

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
        {
            Console.WriteLine("Try");
            throw new Exception();
        }
        finally
        {
            Console.WriteLine("finally");
        }

如果出现终止应用程序的致命异常,则不会调用 Final 块。包括堆栈溢出、要调用的方法的 JIT 期间的异常、CLR 运行时的致命异常。

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