如果finally块抛出异常,到底会发生什么?

具体来说,如果在finally块中途抛出异常会发生什么。这个块中的其余语句(之后)是否被调用?

我知道异常会向上传播。


当前回答

快速(相当明显的)代码片段保存“原始异常”(在try块中抛出)并牺牲“finally异常”(在finally块中抛出),以防原始异常对你更重要:

try
{
    throw new Exception("Original Exception");
}
finally
{
    try
    {
        throw new Exception("Finally Exception");
    }
    catch
    { }
}

当执行上面的代码时,“原始异常”向上传播调用堆栈,“最终异常”丢失。

其他回答

public void MyMethod()
{
   try
   {
   }
   catch{}
   finally
   {
      CodeA
   }
   CodeB
}

CodeA和CodeB抛出的异常的处理方式是相同的。

finally块中抛出的异常没有任何特殊含义,将其视为代码B抛出的异常。

如果finally块抛出异常,到底会发生什么?

该异常向外和向上传播,并且将(可以)在更高的级别进行处理。

finally块在抛出异常之后将不会完成。

如果finally块是在处理之前的异常期间执行的,那么第一个异常将丢失。

c# 4语言规范§8.9.5:如果finally块抛出另一个异常,则当前异常的处理将终止。

在另一个异常活动时抛出异常将导致第一个异常被第二个(稍后)异常所取代。

下面是一些代码,说明发生了什么:

    public static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("first exception");
            }
            finally
            {
                //try
                {
                    throw new Exception("second exception");
                }
                //catch (Exception)
                {
                    //throw;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

运行代码,您将看到“第二个异常” 取消try和catch语句的注释,您将看到“第一个异常” 也取消注释抛出;语句,您将再次看到“第二个异常”。

如果有一个异常挂起(当try块有finally但没有catch时),新异常将替换该异常。

如果没有异常挂起,它就像在finally块外抛出异常一样工作。

对于这样的问题,我通常在Visual Studio中打开一个空的控制台应用程序项目,并编写一个小示例程序:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Inner catch block handling {0}.", ex.Message);
                throw;
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

当您运行程序时,您将看到执行catch和finally块的确切顺序。请注意,在抛出异常后,finally块中的代码将不会被执行(事实上,在这个示例程序中,Visual Studio甚至会警告你,它已经检测到不可访问的代码):

Inner catch block handling exception thrown from try block.
Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block

额外的评论

正如Michael Damatov指出的,try块中的异常如果不在(内部)catch块中处理,就会被“吃掉”。事实上,在上面的例子中,重新抛出的异常并没有出现在外层catch块中。为了更清楚地说明这一点,请看下面稍作修改的示例:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

正如你可以从输出中看到的,内部异常被“丢失”(即被忽略):

Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block