.NET 没有概念性(致命)错误异常类型的设计原因?
Design reason why .NET does not have a conceptual (Fatal-)Error exception type?
初步说明:
这个问题不打算 bash 在 .NET 上,也不打算发起讨论 war 如果有 "Fatal Exception" - Java 的设计者 clearly thought there are,.NET 设计者要么不这么想,要么不知道,要么可能有另一个(技术)原因导致异常层次结构成为现在的样子。
我最感兴趣的是是否有任何 MS 设计人员的设计文档或声明为什么 .NET 层次结构是今天的样子。胡乱猜测和推测会得出错误的答案。有凝聚力的 arguments/examples 为什么没有这样的东西(可分类)Fatal Exception 当然可以做出有效的答案,尽管我肯定不同意他们。但是,与评论相反,我保证不会与任何有凝聚力的答案争论 ;-) 另一类答案 可以 证明 Java 的 Error
类型分类是坏主意 / 在 Java 中实际上不起作用,从而隐含地说明了为什么 .NET 不需要它。 :-)
在努力成为更有经验的 C# 程序员的过程中,我注意到 .NET exception hierarchy:
中的一些我认为相当奇怪的东西 (*)
所有 抛出类型的基础 class 是 Exception
(well, basically anyway).
具体来说,许多异常直接从 Exception
and the further categorization 派生到 SystemException
-> 等等。似乎 毫无意义 有点武断。
举个例子,特别是 对我来说很奇怪似乎 SEHException
是一个 ExternalExpection
是一个 SystemException
当它真的似乎更像是一种崩溃和燃烧的错误。
虽然在 Java 方面没有太多经验,但我发现 Java 的区别。 Error
type vs "normal" Exception
很有意义。细节当然可以争论,但 .NET
/C# 甚至不尝试这种方法似乎很奇怪。
C# 名人 Eric Lippert 有一个 nice piece on categorizing exceptions 我非常同意,这让我更加想知道为什么 .NET 甚至不尝试为 "Fatal Exceptions".[=24 提供一个桶=]
"Fatal Exception" 我基本上暗示了 Lippert 先生描述的相同概念:
Fatal exceptions are not your fault, you cannot prevent them, and you
cannot sensibly clean up from them. ...
注意:最重要的是,它们很可能而且具体来说也不是您调用的引发异常的操作的错误。
... They almost always happen because
the process is deeply diseased and is about to be put out of its
misery. Out of memory, thread aborted, and so on. There is absolutely
no point in catching these because nothing your puny user code can do
will fix the problem. Just let your "finally" blocks run and hope for
the best. (Or, if you're really worried, fail fast and do not let the
finally blocks run; at this point, they might just make things worse.
But that's a topic for another day.)
我会注意到,从技术上讲,某些致命异常 与任何其他异常 完全相同。您应该能够在适当的时候捕获它——只是对于 99% 的代码来说,根本不适合处理这些。
以 Mr. Lipperts 为例:假设我调用一个函数来打开一个可以引发各种异常的文件。如果我发现其中任何一个,我想做的就是报告打开文件失败,原因 X 并适当地继续。 但是 如果打开一个文件引发,比如说,一个 ThreadAbortedException,报告任何东西都没有意义,因为不是文件打开操作失败,而是一些代码中止了当前线程并在与假设的 FileNotFoundException 相同的一点在绝大多数情况下没有意义。
评论者似乎认为只有捕获站点才能真正判断某物是否"fatal"并且没有预先分类是合适的,但我会强烈怀疑有一个很好的异常列表99% 的用户代码从不希望捕获:
以 WhosebugException
(我相信还有更多)为例,这是 "just" 一个常规的 SystemException 硬连线为致命的 :
In the .NET Framework 1.0 and 1.1, you could catch a
WhosebugException object (for example, to recover from unbounded
recursion). Starting with the .NET Framework 2.0, you can’t catch a
WhosebugException object with a try/catch block, and the
corresponding process is terminated by default.
请注意,我不认为致命异常必须立即终止应用程序(相反)。我只问为什么 .NET 框架不尝试在异常层次结构中表示 "fatalness",而设计者显然确实认为某些异常比其他异常更致命。
(*) "consider rather strange" 实际上 意味着我个人,在时间连续体的这个点上,找到了 .NET 异常层次结构totally botched.
首先我要说的是,这对于 Whosebug 来说并不是一个特别好的问题。这不是关于真实代码的特定技术问题,而是寻求设计文档——一种场外资源——或对特定 class 层次结构为何按原样设计的理由。两者都不太适合 SO。
也就是说,我可以在这里发表一些看法。
我认为所有相关人员都对SystemException
和ApplicationException
的存在感到遗憾是一个合理的假设。将异常分为两大类在当时看来是个好主意:由 "the system" 自身生成的异常,以及由系统用户创建的异常。但在实践中,这不是一个有用的区别,因为你对异常所做的事情是 catch 它们,在什么情况下你想捕获 (1) 所有用户生成的异常或(2) 所有系统生成的异常?没有想到任何情况。
这里说明的与其说是特定情况下的设计失败——尽管设计肯定不是很好——而是在单继承 OOP 中,你只能在 "inheritance pivot",因为它在微软经常被称为,一个糟糕的决定可能会伴随你很长时间。
现在回想起来很容易说,我们也许应该使用其他一些支点。您注意到,在我的文章中,我 class 根据捕获异常的方式来确定异常——致命异常未被捕获是因为捕获它们对您没有好处,愚蠢的异常未被捕获是因为它们实际上是调试辅助工具,令人烦恼由于糟糕的 API 设计,必须捕获异常,必须捕获外生异常,因为它们表明世界与您希望的不同。似乎这里有一个更好的pivot的可能性,异常类型表示它是否是致命的,并表示它是否需要被捕获。这当然不是唯一可能的支点,但似乎有道理。可以设计一个静态分析器(在编译器或第三方工具中)来验证异常是否被正确捕获。
这似乎特别有道理,因为 .NET 中当然有一些异常,实际上是超级致命的。您可以捕获堆栈溢出或线程中止,但系统会积极地重新抛出它们。如果以某种方式将其捕获在元数据中,那就太好了。
作为一名语言设计者,尽管我会进一步回顾,这里的根本问题是异常机制本身 过度工作 if not 滥用。
例如,为什么我们需要致命异常是异常?如果某些异常确实是致命的,并且在某些罕见但关键的情况下确实如此,即使程序由于致命异常而停止运行,您也需要代码 运行 ,那么场景可能会上升到您希望 语言 中的语法来捕获这些语义的级别。假设一个 try-fatality
块,其中清理代码 仅 运行 在极不可能发生致命错误的情况下。也许这是个好主意,也许不是,但关键是它是关于 在语言中加入一个特性来解决问题 而不是将越来越多的语义堆积到一个机制上的想法这似乎不太适合它的所有用途。
例如,为什么会有 "boneheaded" 个异常?像 "this reference isn't allowed to be null, you dummy" 或 "you tried to write to a file after closing it, you dummy" 这样愚蠢的异常的存在实际上只是异常,因为它们是第一种情况下类型系统和第二种情况下 API 设计的缺点.理想情况下,不会有愚蠢的异常,因为一开始就不可能用语言来表示错误的程序。可以将代码契约等机制构建到语言中,以帮助确保在编译时捕获 "boneheaded" 异常情况。同样,这也许是个好主意,也许不是,但关键是有机会在语言设计层面解决这个问题,而不是让异常承担这个负担,然后询问如何设计类型层次结构分明代表了一个愚蠢的例外。
初步说明:
这个问题不打算 bash 在 .NET 上,也不打算发起讨论 war 如果有 "Fatal Exception" - Java 的设计者 clearly thought there are,.NET 设计者要么不这么想,要么不知道,要么可能有另一个(技术)原因导致异常层次结构成为现在的样子。
我最感兴趣的是是否有任何 MS 设计人员的设计文档或声明为什么 .NET 层次结构是今天的样子。胡乱猜测和推测会得出错误的答案。有凝聚力的 arguments/examples 为什么没有这样的东西(可分类)Fatal Exception 当然可以做出有效的答案,尽管我肯定不同意他们。但是,与评论相反,我保证不会与任何有凝聚力的答案争论 ;-) 另一类答案 可以 证明 Java 的 Error
类型分类是坏主意 / 在 Java 中实际上不起作用,从而隐含地说明了为什么 .NET 不需要它。 :-)
在努力成为更有经验的 C# 程序员的过程中,我注意到 .NET exception hierarchy:
中的一些我认为相当奇怪的东西 (*)所有 抛出类型的基础 class 是 Exception
(well, basically anyway).
具体来说,许多异常直接从 Exception
and the further categorization 派生到 SystemException
-> 等等。似乎 毫无意义 有点武断。
举个例子,特别是 对我来说很奇怪似乎 SEHException
是一个 ExternalExpection
是一个 SystemException
当它真的似乎更像是一种崩溃和燃烧的错误。
虽然在 Java 方面没有太多经验,但我发现 Java 的区别。 Error
type vs "normal" Exception
很有意义。细节当然可以争论,但 .NET
/C# 甚至不尝试这种方法似乎很奇怪。
C# 名人 Eric Lippert 有一个 nice piece on categorizing exceptions 我非常同意,这让我更加想知道为什么 .NET 甚至不尝试为 "Fatal Exceptions".[=24 提供一个桶=]
"Fatal Exception" 我基本上暗示了 Lippert 先生描述的相同概念:
Fatal exceptions are not your fault, you cannot prevent them, and you cannot sensibly clean up from them. ...
注意:最重要的是,它们很可能而且具体来说也不是您调用的引发异常的操作的错误。
... They almost always happen because the process is deeply diseased and is about to be put out of its misery. Out of memory, thread aborted, and so on. There is absolutely no point in catching these because nothing your puny user code can do will fix the problem. Just let your "finally" blocks run and hope for the best. (Or, if you're really worried, fail fast and do not let the finally blocks run; at this point, they might just make things worse. But that's a topic for another day.)
我会注意到,从技术上讲,某些致命异常 与任何其他异常 完全相同。您应该能够在适当的时候捕获它——只是对于 99% 的代码来说,根本不适合处理这些。
以 Mr. Lipperts 为例:假设我调用一个函数来打开一个可以引发各种异常的文件。如果我发现其中任何一个,我想做的就是报告打开文件失败,原因 X 并适当地继续。 但是 如果打开一个文件引发,比如说,一个 ThreadAbortedException,报告任何东西都没有意义,因为不是文件打开操作失败,而是一些代码中止了当前线程并在与假设的 FileNotFoundException 相同的一点在绝大多数情况下没有意义。
评论者似乎认为只有捕获站点才能真正判断某物是否"fatal"并且没有预先分类是合适的,但我会强烈怀疑有一个很好的异常列表99% 的用户代码从不希望捕获:
以 WhosebugException
(我相信还有更多)为例,这是 "just" 一个常规的 SystemException 硬连线为致命的 :
In the .NET Framework 1.0 and 1.1, you could catch a WhosebugException object (for example, to recover from unbounded recursion). Starting with the .NET Framework 2.0, you can’t catch a WhosebugException object with a try/catch block, and the corresponding process is terminated by default.
请注意,我不认为致命异常必须立即终止应用程序(相反)。我只问为什么 .NET 框架不尝试在异常层次结构中表示 "fatalness",而设计者显然确实认为某些异常比其他异常更致命。
(*) "consider rather strange" 实际上 意味着我个人,在时间连续体的这个点上,找到了 .NET 异常层次结构totally botched.
首先我要说的是,这对于 Whosebug 来说并不是一个特别好的问题。这不是关于真实代码的特定技术问题,而是寻求设计文档——一种场外资源——或对特定 class 层次结构为何按原样设计的理由。两者都不太适合 SO。
也就是说,我可以在这里发表一些看法。
我认为所有相关人员都对SystemException
和ApplicationException
的存在感到遗憾是一个合理的假设。将异常分为两大类在当时看来是个好主意:由 "the system" 自身生成的异常,以及由系统用户创建的异常。但在实践中,这不是一个有用的区别,因为你对异常所做的事情是 catch 它们,在什么情况下你想捕获 (1) 所有用户生成的异常或(2) 所有系统生成的异常?没有想到任何情况。
这里说明的与其说是特定情况下的设计失败——尽管设计肯定不是很好——而是在单继承 OOP 中,你只能在 "inheritance pivot",因为它在微软经常被称为,一个糟糕的决定可能会伴随你很长时间。
现在回想起来很容易说,我们也许应该使用其他一些支点。您注意到,在我的文章中,我 class 根据捕获异常的方式来确定异常——致命异常未被捕获是因为捕获它们对您没有好处,愚蠢的异常未被捕获是因为它们实际上是调试辅助工具,令人烦恼由于糟糕的 API 设计,必须捕获异常,必须捕获外生异常,因为它们表明世界与您希望的不同。似乎这里有一个更好的pivot的可能性,异常类型表示它是否是致命的,并表示它是否需要被捕获。这当然不是唯一可能的支点,但似乎有道理。可以设计一个静态分析器(在编译器或第三方工具中)来验证异常是否被正确捕获。
这似乎特别有道理,因为 .NET 中当然有一些异常,实际上是超级致命的。您可以捕获堆栈溢出或线程中止,但系统会积极地重新抛出它们。如果以某种方式将其捕获在元数据中,那就太好了。
作为一名语言设计者,尽管我会进一步回顾,这里的根本问题是异常机制本身 过度工作 if not 滥用。
例如,为什么我们需要致命异常是异常?如果某些异常确实是致命的,并且在某些罕见但关键的情况下确实如此,即使程序由于致命异常而停止运行,您也需要代码 运行 ,那么场景可能会上升到您希望 语言 中的语法来捕获这些语义的级别。假设一个 try-fatality
块,其中清理代码 仅 运行 在极不可能发生致命错误的情况下。也许这是个好主意,也许不是,但关键是它是关于 在语言中加入一个特性来解决问题 而不是将越来越多的语义堆积到一个机制上的想法这似乎不太适合它的所有用途。
例如,为什么会有 "boneheaded" 个异常?像 "this reference isn't allowed to be null, you dummy" 或 "you tried to write to a file after closing it, you dummy" 这样愚蠢的异常的存在实际上只是异常,因为它们是第一种情况下类型系统和第二种情况下 API 设计的缺点.理想情况下,不会有愚蠢的异常,因为一开始就不可能用语言来表示错误的程序。可以将代码契约等机制构建到语言中,以帮助确保在编译时捕获 "boneheaded" 异常情况。同样,这也许是个好主意,也许不是,但关键是有机会在语言设计层面解决这个问题,而不是让异常承担这个负担,然后询问如何设计类型层次结构分明代表了一个愚蠢的例外。