在finally块中将未处理的异常更改为已处理的异常

用户名

考虑以下程序:

using System;
static class Program {
  static void Main(string[] args) {
    try {
      try { throw new A(); }
      finally { throw new B(); }
    }
    catch (B) { }
    Console.WriteLine("All done!");
  }
}

class A : Exception { }

class B : Exception { }

在这里,A抛出一个没有处理程序的类型异常。在该finally块中,B引发了一个类型为具有处理程序的异常通常,以finally块为单位抛出的异常会获胜,但未处理的异常则有所不同。

调试时,调试器在A抛出时停止执行,并且不允许finally执行块。

如果不进行调试(从命令提示符下独立运行),则会显示一条消息(打印并显示崩溃对话框),其中包含未处理的异常,但是此后,“全部完成!” 确实被打印。

当添加一个顶级异常处理程序,该处理程序除了重新抛出捕获的异常外,其他一切都很好:没有意外消息,并且“全部完成!” 打印。

我了解这是怎么发生的:确定异常是否具有处理程序发生在finally执行任何之前通常这是理想的,并且当前行为是有道理的。finally无论如何,块通常都不应引发异常。

但是这个另一个堆栈溢出问题引用了C#语言规范,并声称需要使用该finally块来覆盖A异常。阅读规范,我同意这正是它所需要的:

  • 在当前函数成员中,try将检查包含抛出点的每个语句。对于每个语句S,从最里面的try语句开始,到最外面的try语句结束,将评估以下步骤:
    • 如果的tryS包含了抛出点,并且如果S具有一个或多个catch子句,则将对catch子句进行检查[...]
    • 否则,如果该try块或的一个catchS包含了抛出点,并且S具有一个finally块,则控制权将转移到该finally块。如果该finally块引发另一个异常,则终止当前异常的处理。否则,当控制到达finally的终点时,将继续处理当前异常。
  • 如果异常处理程序不在当前函数调用中,则该函数调用将终止,并发生以下情况之一:
    • [...]
  • 如果异常处理终止了当前线程中的所有函数成员调用,表明该线程没有该异常的处理程序,则该线程本身将终止。这种终止的影响是实现定义的。

根据我对规范的阅读,直到所有函数调用都终止之后,才将异常视为未处理,并且直到finally处理程序执行后,函数调用才会终止

我在这里遗漏了什么吗,还是Microsoft的C#实现与他们自己的规范不一致?

帕维尔·克雷梅兹(Pavel Krymets)

我认为问题在于.NET异常处理是如何在结构化异常处理(基于结构化异常处理)的基础上构建的,该规则对将其扔到finally块中有所不同。

当发生异常A时,SEH会尝试找到第一个能够处理您的异常类型的处理程序,然后开始运行所有的finally块,然后展开处理,但基于SEH逻辑,则没有此类处理程序,因此它会在.NET发出之前对未处理的异常进行哭泣执行自己的规则。

这解释了解决该问题的顶级处理程序(但仅适用于可以处理异常类型A的处理程序)。

IL本身看起来是有效的:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       49 (0x31)
  .maxstack  1
  IL_0000:  nop
  IL_0001:  nop
  .try
  {
    IL_0002:  nop
    .try
    {
      IL_0003:  nop
      IL_0004:  newobj     instance void A::.ctor()
      IL_0009:  throw
    }  // end .try
    finally
    {
      IL_000a:  nop
      IL_000b:  newobj     instance void B::.ctor()
      IL_0010:  throw
    }  // end handler
  }  // end .try
  catch B 
  {
    IL_0011:  pop
    IL_0012:  nop
    IL_0013:  ldstr      "B"
    IL_0018:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_001d:  nop
    IL_001e:  nop
    IL_001f:  leave.s    IL_0021
  }  // end handler
  IL_0021:  nop
  IL_0022:  nop
  IL_0023:  nop
  IL_0024:  ldstr      "A"
  IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002e:  nop
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::Main

单声道有相同的问题http://ideone.com/VVoPx6

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章