finally 块下面的代码如何执行?

Posted

技术标签:

【中文标题】finally 块下面的代码如何执行?【英文标题】:How code below the finally block execute? 【发布时间】:2021-06-21 18:32:44 【问题描述】:

我找不到任何微软官方资源显示 finally 块下面的代码是如何执行的,关于它的唯一信息是通过 C# 编写的 CLR 一书,作者说:

如果在 try 块中没有抛出异常,或者如果 catch 块捕获到异常并且不抛出或重新抛出异常,则执行 finally 块下面的代码。

假设我们有以下代码:

class Program 
   static void Main(string[] args) 
      SomeMethod1();
      Console.ReadLine();
   

   static void SomeMethod1() 
      try 
         SomeMethod2();
      
      finally 
         Console.WriteLine("SomeMethod1 finally");
      
      Console.WriteLine("SomeMethod1 last");
   

   static void SomeMethod2() 
      try 
         SomeMethod3();
      
      catch (DivideByZeroException e) 
         Console.WriteLine("SomeMethod2 caught");
      
      finally 
         Console.WriteLine("SomeMethod2 finally");
      
      Console.WriteLine("SomeMethod2 last");
   

   static void SomeMethod3() 
      try 
         SomeMethod4();
      
      finally 
         Console.WriteLine("SomeMethod3 finally");
      
      Console.WriteLine("SomeMethod3 last");
   

   static void SomeMethod4() 
      try 
         Int32 i = 0;
         var c = 3 / i;
      
      finally 
         Console.WriteLine("SomeMethod4 finally");
      
      Console.WriteLine("SomeMethod4 last");
   

输出是:

SomeMethod4 finally
SomeMethod3 finally
SomeMethod2 caught
SomeMethod2 finally
SomeMethod2 last
SomeMethod1 finally
SomeMethod1 last

您可以看到“SomeMethod4 last”和“SomeMethod3 last”没有被打印出来。 “SomeMethod4 last”没有打印好理解,因为SomeMethod4抛出异常,并且没有catch块来捕获异常,所以不符合作者规定的要求,还算公平。

但是为什么“SomeMethod3 last”没有被打印出来? SomeMethod3 没有抛出异常,就像 SomeMethod1 一样,那么为什么打印“SomeMethod1 last”而“SomeMethod3 last”没有?是否有任何 Microsoft 官方资源可以解释其机制?

【问题讨论】:

【参考方案1】:

SomeMethod4 中抛出异常时,它会向上流向调用方法SomeMethod3。把它想象成SomeMethod4 实际上是像这样内联在SomeMethod3 中的。

    static void SomeMethod3()
    
        try
        
            try
            
                Int32 i = 0;
                var c = 3 / i;
            
            finally
            
                Console.WriteLine("SomeMethod4 finally");
            
            Console.WriteLine("SomeMethod4 last");
        
        finally
        
            Console.WriteLine("SomeMethod3 finally");
        
        Console.WriteLine("SomeMethod3 last");
    

SomeMethod2 Last 运行的原因是因为它是实际捕获异常的部分。

如果你给SomeMethod3添加一个catch,你会看到SomeMethod3 Last也被打印出来了。

异常会中断应用程序的流程,并且在到达 catch 语句之前基本上不会停止返回调用堆栈。从技术上讲,异常是在SomeMethod1 中引发的,但它在SomeMethod2 中被捕获,这意味着异常永远不会到达SomeMethod1

【讨论】:

@Lopop 感谢您的回答,但为什么要打印“SomeMethod1 last”? @amjad 我对帖子的末尾进行了编辑,这可能为时已晚。但原因是因为 SomeMethod2 正在捕获异常。一旦捕获到异常,它就不会继续向上调用堆栈。通过像你一样捕捉它,你实际上是在说“我可以处理这个,不要让其他人知道发生了这个异常”,这就是 SomeMethod1 不受该异常影响的原因。 谢谢。我现在看到了,但不明白为什么没有任何官方的微软资源提到它,他们所有的例子在 finally 块下面都没有代码。 在此处查看此文档...虽然他们没有在 finally 下方添加任何代码,但它们确实包含了一个注释,该注释引用了在 finally 之后运行的更多代码。 docs.microsoft.com/en-us/dotnet/csharp/language-reference/…

以上是关于finally 块下面的代码如何执行?的主要内容,如果未能解决你的问题,请参考以下文章

return和finally究竟谁先执行,还有return是怎么返回数据的

Java finally语句到底是在return之前还是之后执行

try/catch/finally

try catch finally return

finally小知识点

java中finally语句快会不会执行的问题