为什么在C#6.0中交换内部'finally'和'outer'的执行顺序?

我见过这个例子:

static void Main(string[] args) { Console.WriteLine("Start"); try { SomeOperation(); } catch (Exception) when (EvaluatesTo()) { Console.WriteLine("Catch"); } finally { Console.WriteLine("Outer Finally"); } } private static bool EvaluatesTo() { Console.WriteLine($"EvaluatesTo: {Flag}"); return true; } private static void SomeOperation() { try { Flag = true; throw new Exception("Boom"); } finally { Flag = false; Console.WriteLine("Inner Finally"); } } 

哪个产生下一个输出:

 Start EvaluatesTo: True Inner Finally Catch Outer Finally 

这听起来很奇怪,我正在寻找这个命令的一个很好的解释来把它包装在我脑海中。 我期待的finally块之前执行时间:

 Start Inner Finally EvaluatesTo: True Catch Outer Finally 

该文档指出,这个执行顺序是正确的,但没有详细说明为什么这样做,以及这里的执行顺序的规则是什么。

你可能已经被教导,当exception处理发生时,每种方法都被分开考虑。 也就是说,因为你的内部方法有一个try...finally ,任何exception将首先触发finally ,然后它会“看”一个更高的try 。 这是不正确的。

从CLR的ECMA规范(ECMA-335,I.12.4.2.5exception处理概述):

发生exception时,CLI将在arrays中search第一个受保护的块

  • 保护包含当前指令指针的区域
  • 是一个catch处理程序块
  • 谁的filter希望处理这个exception

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

如果find匹配,则CLI将堆栈移回刚刚find的点,但是这次调用finally和fault处理程序。 然后启动相应的exception处理程序。

正如你所看到的,行为是100%符合规范。

  1. 寻找一个受保护的块 – trySomeOperation
  2. 它有一个catch处理程序块吗? 没有。
  3. 在调用方法中寻找受保护的块 – 在Main try
  4. 它有一个catch处理程序块吗? 是!
  5. filter是否希望处理exception? 评估filter( 免责声明 :这并不意味着保护区块中的所有filter将始终被评估 – 如果filter没有副作用,那么这个filter是没有问题的,当然这不会有问题),结果是是。
  6. 走回堆栈并执行所有的最终和error handling程序
    1. finallySomeOperation

当然, Mainfinally并不是其中的一部分,当执行离开受保护块时,它将执行,无论是否有exception。

编辑:

只是为了完整性 – 这一直是这样的。 唯一改变的是C#现在支持exceptionfilter,它允许你观察执行的顺序。 VB.NET支持版本1的exceptionfilter。

finally块总是执行,不pipe是否抛出exception。

finally块执行以下任一操作:

  • 一个catch块完成后
  • 由于跳转语句(例如返回或转到),控制离开try块后,
  • try块结束后

唯一能够击败finally块的东西是一个无限循环或突发的进程。 finally块有助于确定一个程序