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

问题描述 投票:0回答:6

我对 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();
    }
 }

异常发生在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();
    }
}
c# exception
6个回答
5
投票
当控制因任何原因

离开其所附着的

finally
块时,
try 执行;不仅仅是同一级别有一个 catch
 块 - 那将是一个 
fault
 处理程序(用 CLR 术语来说),我认为我们在 C# 中仍然无法做到这一点。

因此它进入打印

finally

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


请注意,如果代码中的任何地方完全未捕获异常,则可能会出现一些奇怪的情况。异常处理分两个阶段进行。在第一阶段,它尝试为异常找到适当的

catch

 子句,其中可以包括执行保护子句(
when
、C#6 及更高版本)。在第二阶段,它会展开堆栈并根据嵌套需要执行任何 
finally
 子句,然后到达定义处理异常的 
catch
 子句的正确级别
1

我相信,在某些环境中,如果第一阶段根本无法找到合适的异常处理程序(

catch

子句)
,整个托管环境可能会被拆除。在这种情况下,finally子句不会被执行。

符合 CLR 标准的所有

要求

都在 MS Partition I.pdf 文档中进行了描述,该文档可以在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; } } } }

打印:

Start Outer Try Start Inner Try In outer guard In Inner Finally In outer catch In outer finally Complete

说明异常处理的两遍性质(
In outer guard

打印于

before
In Inner Finally
    


1
投票
finally

(A)在

catch
(B)之前运行的原因是,当抛出异常时,您在外部块之前退出内部块(导致
finally
运行)块的
catch
开始发挥作用。 考虑这段代码:

private void TestTryCatchFinally() { try { Debug.WriteLine("Start Outer Try"); try { Debug.WriteLine("Start Inner Try"); throw new Exception("Exception from inner try"); Debug.WriteLine("End of Inner Try - never reaced"); } //remove this catch block for second test catch (Exception) { Debug.WriteLine("In inner catch"); } //end of code to remove finally { Debug.WriteLine("In Inner Finally"); } } catch (Exception) { Debug.WriteLine("In outer catch"); } finally { Debug.WriteLine("In outer finally"); } }

如果我运行此代码,我会得到以下输出:

Start Outer Try Start Inner Try Exception thrown: 'System.Exception' in MyTestApp.exe In inner catch In Inner Finally In outer finally

这正是您所期望的。  但是,如果我删除内部 catch 块(如代码中所述),我会得到以下输出:

Start Outer Try Start Inner Try Exception thrown: 'System.Exception' in MyTestApp.exe In Inner Finally In outer catch In outer finally

在这种情况下,一旦执行退出内部 try 块,finally 代码就会执行。  然后轮到外部捕获块了。


0
投票


0
投票

class Program { public static void Main(string[] args) { try { int a = 2; int b = 10 / a; if (a == 1) a = a / a - a; if (a == 2) { int[] c = { 1 }; c[8] = 9; } } catch (IndexOutOfRangeException e) { //Do something when something in try block throws error Console.WriteLine("B"); } finally { //This code will ALWAYS execute //Even when there is no error, A will be written to console Console.WriteLine("A"); } Console.ReadLine(); } }



0
投票

请注意,异常一旦创建就存在,除非它被捕获并处理。如果代码没有捕获它 - 将会导致意外的结果或错误


-1
投票

try { } catch (Exception ex) { } finally { }

通过使用
finally{}

块,您可以确保某些语句将始终运行

    

© www.soinside.com 2019 - 2024. All rights reserved.