23

我的理解是基于这篇很长但很棒的文章,它支持 C# 规范中列出的行为。

CLI 标准 (ECMA-335) 表明,如果没有合适的捕获,运行时应立即终止。.NET 运行时不这样做,相反,它似乎倾向于 C# 规范 (EMC-334) 的行为。

首先,我觉得奇怪的是语言规范似乎正在定义框架行为。其次,他们似乎自相矛盾。

  • 它们是否相互矛盾,还是我理解了文档的错误含义?
  • 运行时是否必须以这种方式进行异常处理才能符合标准?

作为一个可选问题,哪一个是“正确”的,例如,如果我要编写自己的 CLI 实现,我应该使用哪一个?请注意,EMCA-335 (CLI) 文档是两个月前更新的,而 EMCA-334 (C#) 是在 2006 年更新的。


ECMA-335 分区 I 第 12.4.2.5 节

  • 发生异常时,CLI 会在数组中搜索第一个受保护的块
    • 保护包括当前指令指针的区域和
    • 是一个 catch 处理程序块并且
    • 谁的过滤器希望处理异常
  • 如果在当前方法中未找到匹配项,则搜索调用方法,依此类推。如果未找到匹配项,CLI 将转储堆栈跟踪并中止程序。

  • 如果找到匹配项,CLI 将堆栈返回到刚刚找到的点,但这次调用 finally 和故障处理程序。然后它启动相应的异常处理程序。

C# 规范 §15.9.5 和 §15.10MSDN 上的§8.9.5 和 §8.10 )

它与 CLI 标准之间的主要区别在于,无论是否找到了 catch 块,应用程序都将不仅存在,而且仍会展开堆栈,并处理 finally 处理程序。

我建议阅读标准本身以更好地理解这一点,因为下面是一个非常粗略的总结。它逐步概述了如何在每种可能的情况下执行 try 语句。

  • 在引发异常的函数中:
    • 在每个 try 语句中查找匹配的 catch 子句
      • 如果存在,则执行 catch 语句
    • finally 块如果存在则执行
  • 如果没有处理程序,则在调用函数中重复上述步骤
  • 如果异常处理终止了当前线程中的所有函数成员调用,表明该线程没有异常处理程序,则该线程本身终止。这种终止的影响是实现定义的。
4

3 回答 3

5

这里没有冲突。C# 语言规范的措辞如下:

如果try 语句没有catch 子句或没有catch 子句匹配异常:
• 如果try 语句有finally 块,则执行finally 块。
• 异常传播到下一个封闭的try 语句。

Bullet 2 这里特别没有说明没有下一个封闭的 try 语句时会发生什么。为此,请转到 8.9.5 的末尾:

如果异常处理终止了当前线程中的所有函数成员调用,表明该线程没有异常处理程序,则该线程本身终止。这种终止的影响是实现定义的。

它当然是实现定义的。除了 Ecma 335 规范之外,异常处理策略是 Microsoft CLR 中的一个可配置项。由 ICLRPolicyManager::SetActionOnFailure() 控制。<legacyUnhandledExceptionPolicy>进而使用app.exe.config 文件元素在默认主机中进行配置。CLR 2.0 及更高版本的默认设置是立即终止程序。

否则,这是相当低效的圣经诠释学。对于 C# 程序员来说,这一切都不足为奇,尤其是考虑到它是多么容易测试。

于 2012-08-29T00:13:33.960 回答
3

我认为这可能只是一个模糊的措辞问题。

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

好的,在 C# 中确实如此。我们都知道,如果我们没有catchthen 异常会导致我们的程序崩溃。

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

这也符合我们从 C# 中知道的内容。如果有一些finally(我们看不到fault)块需要处理,因为我们从抛出异常到我们的catch块向上堆栈,它们被处理,但它停在那里并且不再向上堆栈。

很多事情取决于我们如何阅读我刚刚引用的第二段摘录开头的“如果”。您将其阅读为“如果……那么……否则没有这样的事情”。虽然它可以被解读为第一个摘录,标识堆栈中将被走到的点:如果有 a catch,那么它会走到那个点。如果没有捕获,那么它会走到堆栈的最顶端,我们会得到一个转储并中止。finally 处理程序(和故障处理程序)仍然被调用,但重点不是匹配的 catch 处理程序。

您的阅读是最字面的,而我的阅读则有点延伸。但是,我的确实与finally同一标准中其他地方的描述相匹配,最接近

于 2012-08-29T00:26:28.880 回答
0

OP中引用的文章有一个不正确的基本假设:

当然,如果不首先考虑 Windows 结构化异常处理 (SEH),我们就不能谈论托管异常。我们还需要查看 C++ 异常模型。这是因为托管异常和 C++ 异常都是在底层 SEH 机制之上实现的,并且因为托管异常必须与 SEH 和 C++ 异常互操作。

CLR 标准 (ISO 23271/ECMA 335) 有意与平台无关。Microsoft 的实现是许多可能的实现之一(当然,Mono 是另一种)。

我敢肯定,与 Windows 结构化异常处理和 C++ 异常处理的互操作性是 Microsoft 的选择,而不是 ISO 23271 的要求。

于 2012-08-29T02:38:04.163 回答