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

时间:2016-08-02 12:26:32

标签: c# c#-6.0

我见过这个例子:

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之前执行when块:

Start
Inner Finally
EvaluatesTo: True
Catch
Outer Finally

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

2 个答案:

答案 0 :(得分:38)

您可能已经被告知,在发生异常处理时,会分别考虑每种方法。也就是说,由于您的内部方法有try...finally,因此任何异常都会首先触发finally,然后它会"看起来"为try更高。这不是真的。

来自ECR的CLR规范(ECMA-335,I.12.4.2.5异常处理概述):

  

发生异常时,CLI会在数组中搜索

的第一个受保护块      
      
  • 保护包含当前指令指针
  • 的区域   
  • 是一个catch处理程序块
  •   
  • 谁的过滤器希望处理异常
  •   
     

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

     

如果找到匹配项,CLI会将堆栈移回到刚刚找到的位置,但这次调用finally和fault处理程序。然后它启动相应的异常处理程序。

如您所见,该行为100%符合规范。

  1. try
  2. 中查找受保护的区块SomeOperation
  3. 它是否有一个catch处理程序块?否。
  4. 在调用方法中查找受保护的块 - try
  5. 中的Main
  6. 它是否有一个catch处理程序块?是的!
  7. 过滤器是否希望处理异常?对过滤器进行评估(免责声明:这并不意味着保护块中的所有过滤器将始终被评估 - 如果过滤器没有副作用就没有问题,它实际上不应该这样做&#39 ;当然),结果是肯定的。
  8. 返回堆栈并执行所有finally和故障处理程序
      finally
    1. SomeOperation
  9. finally中的Main当然不是其中的一部分 - 它会在执行离开受保护的块时执行,而不管异常。

    修改

    只是为了完整 - 这一直是这样的。唯一改变的是C#现在支持异常过滤器,它允许您观察执行的顺序。 VB.NET支持版本1的异常过滤器。

答案 1 :(得分:-5)

无论是否抛出异常,finally块始终都会执行。

finally块执行:

  • 在捕获块完成后
  • 控制因跳转声明(例如,返回或转到)而离开try块后
  • 尝试块结束后

唯一可以击败finally块的东西是无限循环或突然发送的进程。 finally块有助于为程序添加确定性