【发布时间】:2015-01-17 00:54:20
【问题描述】:
考虑这个程序:
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 块通常不应该抛出异常。
但是this other Stack Overflow question 引用了 C# 语言规范并声称需要 finally 块来覆盖 A 异常。阅读规范,我同意这正是它所需要的:
- 在当前函数成员中,检查每个包含抛出点的
try语句。对于每个语句S,从最里面的 try 语句开始到最外面的 try 语句结束,评估以下步骤:
- 如果
S的try块包含抛出点并且如果S 有一个或多个catch子句,则检查catch 子句[...]- 否则,如果
try块或S的catch块包含抛出点,并且如果S具有finally块,则控制将转移到finally块。如果finally块抛出另一个异常,则终止当前异常的处理。否则,当控制到达finally块的终点时,继续处理当前异常。- 如果在当前函数调用中未找到异常处理程序,则函数调用将终止,并出现以下情况之一:
- [...]
- 如果异常处理终止了当前线程中的所有函数成员调用,表明该线程没有异常的处理程序,则该线程本身终止。这种终止的影响是由实现定义的。
根据我对规范的阅读,直到所有函数调用都已终止,并且函数调用在 finally 处理程序执行之前不会终止,否则不会将异常视为未处理.
是我遗漏了什么,还是微软的 C# 实现与他们自己的规范不一致?
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
-
措辞有点笨拙,可能是故意的。但是 CLR 规范对此毫无疑问,第 I.12.4.2.5 章清楚地表明 finally 块仅在 搜索并找到处理程序之后才会执行。
标签: c# .net exception-handling language-lawyer