为什么在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%符合规范。
- 寻找一个受保护的块 –
try
在SomeOperation
- 它有一个catch处理程序块吗? 没有。
- 在调用方法中寻找受保护的块 – 在
Main
try
- 它有一个catch处理程序块吗? 是!
- filter是否希望处理exception? 评估filter( 免责声明 :这并不意味着保护区块中的所有filter将始终被评估 – 如果filter没有副作用,那么这个filter是没有问题的,当然这不会有问题),结果是是。
- 走回堆栈并执行所有的最终和error handling程序
-
finally
在SomeOperation
-
当然, Main
的finally
并不是其中的一部分,当执行离开受保护块时,它将执行,无论是否有exception。
编辑:
只是为了完整性 – 这一直是这样的。 唯一改变的是C#现在支持exceptionfilter,它允许你观察执行的顺序。 VB.NET支持版本1的exceptionfilter。
finally
块总是执行,不pipe是否抛出exception。
finally块执行以下任一操作:
- 一个catch块完成后
- 由于跳转语句(例如返回或转到),控制离开try块后,
- try块结束后
唯一能够击败finally块的东西是一个无限循环或突发的进程。 finally块有助于确定一个程序