【问题标题】:Does exception handling in C# contradict the ECMA-335 standard?C# 中的异常处理是否与 ECMA-335 标准相矛盾?
【发布时间】:2012-08-23 12:36:55
【问题描述】:

我的理解基于this long, but fantastic, article,它支持 C# 规范中列出的行为。

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

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

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

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


ECMA-335 Partition I Section 12.4.2.5

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

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

C# Specification §15.9.5 and §15.10 (§8.9.5 and §8.10 on MSDN)

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

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

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

【问题讨论】:

  • +1 不仅要彻底阅读一份规范,还要阅读两份!也很有趣的问题:)
  • 如果线程终止(或中止程序),异常不会传播到 try 语句之外并且没有控制离开 try 语句——线程只是结束控制。我不认为有矛盾。

标签: c# .net exception-handling specifications


【解决方案1】:

这里没有冲突。 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# 程序员来说,这一切都不足为奇,尤其是考虑到它是多么容易测试。

【讨论】:

  • 实际上,第 2 条在 8.9.5 节中有说明。它详细概述了异常传播以及应该发生的情况。它解释了在传播异常之后(它引用了 8.10 中 try 语句的执行列表),线程将被终止。
  • C# 规范还包括“如果搜索匹配的 catch 子句到达最初启动线程的代码,则终止线程的执行。这种终止的影响是实现定义的”.. .
【解决方案2】:

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

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

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

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

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

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

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

【讨论】:

  • 事实证明这是正确的,只是措辞模糊。我是在最字面意义上阅读,这显然不是行为。但是,似乎标准(正确地)依赖于阅读整个部分的人。 I.12.4.2 说 finally 处理程序将发生,无论它是正常的还是未处理的异常。完全没有实现定义,CLI 标准需要 C# 规范中列出的行为。我应该阅读整个部分。我现在不觉得害羞吗? :X
  • @ChristopherCurrens 无需感到羞怯。我仍然认为您指出的措辞有缺陷。到时候应该就清楚了。您是否提出了勘误表?
  • 我什至没有推荐一个。也许它应该由某人完成。我不知道如何提交它......虽然我想我实际上可以寻找它。
【解决方案3】:

O.P. 中的 cited article 有一个不正确的基本假设:

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

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

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

【讨论】:

  • 对于一篇关于特定实现的文章,我不认为这是一个不正确的假设,而是一个适当的焦点。
猜你喜欢
  • 1970-01-01
  • 2017-07-04
  • 1970-01-01
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多