它似乎按照一些初始测试进行,但我想知道它是否可以保证返回,或者在某些情况下它是否不能返回?这对我的应用程序至关重要,但我还没有找到不会返回的用例。
我想获得这方面的专业知识。
4 回答
其他答案有很多不准确之处。
当控制正常离开 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 的目标吗?” 你会看到聚会上谁阅读了可达性规范,谁没有!
在正常情况下,无论 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”,并且由于 StackOverflow 导致进程终止。
未处理的异常
try
{
Console.WriteLine("Try");
throw new Exception();
}
finally
{
Console.WriteLine("finally");
}
在终止应用程序的致命异常的情况下,将不会调用 finally 块。包括堆栈溢出、JIT 调用方法期间的异常、CLR 运行时中的致命异常。
正如@mintech 指出的那样,如果应用程序挂在块内,它根本不会到达 finally 块。这包括等待同步对象、死锁无限循环甚至是无法关闭它的 UI。