Will code in a Finally statement fire if I return a value in a Try block?
我正在为一个朋友检查一些代码,并说他在tryfinally块内使用了一个返回语句。即使try块的其余部分没有,finally部分中的代码是否仍会触发?
例子:
1 2 3 4 5 6 7 8 9 10 11 12 | public bool someMethod() { try { return true; throw new Exception("test"); // doesn't seem to get executed } finally { //code in question } } |
简单回答:是的。
通常,是的。最后一节保证执行发生的任何操作,包括异常或返回语句。此规则的异常是线程上发生的异步异常(
要进一步了解异步异常和这种情况下的可靠代码,请阅读有关受约束执行区域的内容。
下面是一个小测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Class1 { [STAThread] static void Main(string[] args) { Console.WriteLine("before"); Console.WriteLine(test()); Console.WriteLine("after"); } static string test() { try { return"return"; } finally { Console.WriteLine("finally"); } } } |
结果是:
1 2 3 4 | before finally return after |
从msdn报价
finally is used to guarantee a statement block of code executes regardless of how the preceding try block is exited.
一般来说是的,最后会运行。
对于以下三种情况,最终将始终运行:
以下场景中,最终将不运行:
异步StackOverflowException。从.NET 2.0开始,堆栈溢出将导致进程终止。除非应用了进一步的约束以使finally成为CER(受约束的执行区域),否则finally将不会运行。一般用户代码中不应使用CER。只有在清理代码始终运行是非常关键的情况下才应该使用它们——在所有进程都在堆栈溢出时关闭之后,因此默认情况下将清理所有托管对象。因此,CER应该与之相关的唯一地方是分配给进程外部的资源,例如非托管句柄。
通常,非托管代码在被用户代码使用之前由某个托管类包装。托管包装类通常使用SafeHandle包装非托管句柄。SafeHandle实现了一个关键的终结器和一个在CER中运行的发布方法,以确保清理代码的执行。出于这个原因,你不应该看到CER在用户代码中乱丢东西。
因此,finally不在stackOverflowException上运行的事实应该不会对用户代码产生任何影响,因为进程无论如何都会终止。如果您有一些边缘情况需要清理一些非托管资源(在SafeHandle或CriticalFinalizerObject之外),那么请按如下方式使用CER;但请注意,这是一种糟糕的做法--非托管概念应通过设计抽象为托管类和适当的SafeHandle。
例如。,
1 2 3 4 5 6 7 8 9 10 11 | // No code can appear after this line, before the try RuntimeHelpers.PrepareConstrainedRegions(); try { // This is *NOT* a CER } finally { // This is a CER; guaranteed to run, if the try was entered, // even if a StackOverflowException occurs. } |
有一个非常重要的例外,我在其他答案中没有提到,而且(在用C语言编程18年后)我不能相信我不知道。
如果你在你的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static void Main(string[] args) { Console.WriteLine("Beginning demo of how finally clause doesn't get executed"); try { Console.WriteLine("Inside try but before exception."); throw new Exception("Exception #1"); } catch (Exception ex) { Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception)."); throw; } finally { Console.WriteLine("This never gets executed, and that seems very, very wrong."); } Console.WriteLine("This never gets executed, but I wasn't expecting it to."); Console.ReadLine(); } |
我相信这是有原因的,但奇怪的是,它并没有被更广泛地知道。(例如,这里已经提到,但在这个特定问题中没有提到。)
我意识到我迟到了,但在引发异常的场景中(不同于操作示例),msdn状态(https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx):"如果未捕获异常,则最终块的执行取决于操作系统是否选择触发异常释放操作。"
只有当调用堆栈上的某个其他函数(如main)捕获异常时,才保证执行finally块。这个细节通常不是问题,因为所有的运行时环境(clr和os)c程序都是在进程退出时拥有的大多数空闲资源(文件句柄等)上运行的。在某些情况下,它可能是至关重要的:一个数据库操作正在进行中,您希望提交resp。或者一些远程连接,这些连接可能不会被操作系统自动关闭,然后阻塞服务器。
对。事实上,这就是最后陈述的要点。除非发生了某种类型的情况(内存不足、计算机未插电等),否则应始终执行finally语句。
它也不会触发未捕获的异常并在Windows服务中承载的线程中运行。
在Windows服务中运行的线程中不执行finally
如果您退出应用程序时使用系统出口(0);如
1 2 3 4 5 6 7 8 9 | try { System.out.println("try"); System.exit(0); } finally { System.out.println("finally"); } |
结果是:尝试
是的,最后打电话。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static bool someMethod() { try { return true; throw new Exception("test"); // doesn't seem to get executed } finally { //code in question } } |
finally块的主要目的是执行其中写入的内容。它不应该依赖于Try或Catch中发生的任何事情。但是,对于System.Environment.Exit(1),应用程序将退出,而不移动到下一行代码。
99%的场景可以保证
这种情况非常罕见,但这只是为了表明答案并不总是"是",大多数情况下是"是",有时在罕见情况下是"否"。