C# 异常处理finally 块在catch 块之前

Sha*_*ood 1 c# exception

我对 C# 中如何抛出异常感到非常困惑。如果在 try 块中发生异常,1.它会被抛出到 catch 块,2.当且仅当 catch 块捕获它时,finally 块才会被执行。3.finally 块最后执行,前提是 catch 语句捕获了它。

但是,当我尝试运行下面的程序时,输出是A,B而不是BA。我的理解有问题吗?谢谢。

class Program
 {
     public static void Main(string[] args)
     {
         try
         {
             int a = 2;
             int b = 10 / a;
             try
             {
                 if (a == 1)
                     a = a / a - a;
                 if (a == 2)
                 {
                     int[] c = { 1 };
                     c[8] = 9;
                 }
             }
             finally
             {
                 Console.WriteLine("A");
             }
        }
        catch (IndexOutOfRangeException e)
        {
             Console.WriteLine("B");
        }
        Console.ReadLine();
    }
 }
Run Code Online (Sandbox Code Playgroud)

异常发生在a==2处,我知道外层catch会捕获这个异常。然而finally是先被执行的吗?有什么理由说明为什么会出现这种情况吗?

已编辑

从 C# 文档中我们知道无论是否发生异常,Finally 块都会执行。

但是,我的finally块永远不会被执行,作为回报我收到运行时错误

class Program
{
    public static void Main(string[] args)
    {
        try
        {
            int a = 2;
            int b = 10 / a;
            try
            {
                if (a == 1)
                    a = a / a - a;
                if (a == 2)
                {
                    int[] c = { 1 };
                    c[8] = 9;
                }
            }
            finally
            {
                Console.WriteLine("A");
            }
        }

        finally{
            Console.WriteLine("finally");
        }
        
        Console.ReadLine();
    }
}
Run Code Online (Sandbox Code Playgroud)

Dam*_*ver 5

finally当控制因任何原因try离开其所附着的块时执行;不仅仅是同一级别有一个块 - 那将是一个处理程序(用 CLR 术语),我认为我们在 C# 中仍然无法做到这一点。catchfault

因此它进入了finally打印的内容A,因为控制正在离开该try块。它之所以离开它是因为外部块中捕获了异常catch。但这不会改变任何时间/顺序。


请注意,如果代码中的任何地方完全未捕获异常,则可能会出现一些奇怪的情况。异常处理分两个阶段进行。在第一阶段,它尝试catch为异常找到适当的子句,其中可能包括执行保护子句(whenC#6 及更高版本)。在第二阶段,它会展开堆栈并执行finally嵌套所需的任何子句,然后到达定义catch处理异常的子句的正确级别1

我相信,在某些环境中,如果第一阶段根本无法找到合适的异常处理程序(catch子句),则整个托管环境可能会被拆除。在这种情况下,这些条款不会被执行。finally

MS Partition I.pdf 文档中描述了符合 CLR 的标准所需的所有内容,该文档可在ECMA C# 和公共语言基础结构标准上找到。第 12.4.2.5 节规定:

当发生异常时,CLI 会在数组中搜索第一个受保护的块

  • 保护一个区域,包括当前指令指针

  • 是一个 catch 处理程序块并且

  • 谁的过滤器希望处理异常

如果在当前方法中未找到匹配项,则搜索调用方法,依此类推。如果未找到匹配项,CLI 将转储堆栈跟踪并中止程序。

(我的重点


1使用Flydog57 示例的变体以及 C# 7 本地函数进行说明:

using System;

namespace PlayAreaCSCon
{
    internal class Program
    {
        static void Main(string[] args)
        {
            TestTryCatchFinally();
            Console.WriteLine("Complete");
            Console.ReadLine();
        }

        private static void TestTryCatchFinally()
        {
            try
            {
                Console.WriteLine("Start Outer Try");
                try
                {
                    Console.WriteLine("Start Inner Try");
                    throw new Exception("Exception from inner try");
                }
                finally
                {
                    Console.WriteLine("In Inner Finally");
                }
            }
            catch (Exception ex) when (GuardHelper(ex))
            {
                Console.WriteLine("In outer catch");
            }
            finally
            {
                Console.WriteLine("In outer finally");
            }

            bool GuardHelper(Exception ex)
            {
                Console.WriteLine("In outer guard");
                return true;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这打印:

Start Outer Try
Start Inner Try
In outer guard
In Inner Finally
In outer catch
In outer finally
Complete
Run Code Online (Sandbox Code Playgroud)

说明异常处理的两遍性质(即之前In outer guard打印的) In Inner Finally