2016-08-02 35 views
44

我已经看到了这个例子:为什么在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 

这听起来怪我,我期待这个顺序来包装它的一个很好的解释在我的脑海中。我期待when前要执行的finally块:

Start 
Inner Finally 
EvaluatesTo: True 
Catch 
Outer Finally 

的文档指出这个执行顺序是正确的,但它并没有在它为什么这样做了阐述和究竟是执行规则在此点餐。

+0

@RahulTripathi不,我的问题是关于在C#6.0中交换执行顺序,这个问题在6.0之前有一个在香草C#中的直接执行顺序 – Archeg

+3

这是一个不可避免的结果,当*子句起作用时。 CLR无法确定最终需要执行的内容,直到*找出catch子句将处理异常之后。只有这样它才能开始展开堆栈,最终执行块。 when表达式的可能的无意的副作用是为什么在v6之前它被排除在C#语言之外。 –

+0

@HansPassant具体而言,这是如此:“CLR无法弄清楚最终需要执行的内容,直到它找出catch子句将处理异常为止。”? –

回答

36

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

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

当异常发生时,CLI搜索所述第一受保护的块阵列

  • 保护包括当前指令指针
  • 的区域是catch处理方框
  • 其过滤希望处理异常

如果在当前的方法没有找到匹配,调用的方法搜索,等等。如果找不到匹配,CLI将转储堆栈跟踪并中止程序。

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

正如您所看到的,该行为与规范100%兼容。

  1. 寻找一个保护块 - trySomeOperation
  2. 它有一个catch处理程序块?号
  3. 寻找在调用方法保护块 - tryMain
  4. 它有一个catch处理程序块?是!
  5. 过滤器是否希望处理异常?评估过滤器(免责声明:这并不意味着保护区块中的所有过滤器将始终被评估 - 如果过滤器没有任何副作用,它当然不应该出现问题),以及结果是肯定的。
  6. 走栈背部和执行所有最终和故障处理
    1. finallySomeOperation

Mainfinally是不是其中的一部分,当然 - 它会执行时,执行不管什么异常,都会离开受保护的块。

编辑:

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

+0

哇。事实证明,我不知道如何最终在C#中实际工作。看起来似乎有很多人做过。你知道为什么C#团队决定像这样实现它的任何解释,而不是你如何描述它在第一段? – Archeg

+0

@Archeg - 我还在努力的一件事是你的论点,这在C#6.0中发生了某种变化......你是说这是*而不是*它之前的表现如何? –

+0

@Archeg嗯,我想你从来没有在Windows上使用过结构化的异常处理:)其要点是,这种行为在Windows上可以免费使用,并且异常过滤器不应该引起副作用,那么为什么你会做另一种方式? – Luaan

-5

A finally块总是执行,不管是否引发异常。

finally块来执行两种:

  • 后catch块完成
  • 控制离开,因为跳转语句的try块后(例如,返回或转到)
  • try块结束后

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

+2

我不明白这是如何回答这个问题的。你提供了关于'finally'块的一般性解释,当问题是非常具体的,并且实际上是关于'when'时。 – svick

相关问题