【问题标题】:Why are Exceptions not Checked in .NET?为什么 .NET 中不检查异常?
【发布时间】:2008-09-23 21:52:24
【问题描述】:

我知道用谷歌搜索可以找到合适的答案,但我更喜欢听听您的个人(也许是技术)意见。
Java 和 C# 在抛出异常方面的差异的主要原因是什么?
在 Java 中,抛出异常的方法的签名必须使用“throws”关键字,而在 C# 中,您在编译时不知道是否会抛出异常。

【问题讨论】:

    标签: java .net exception


    【解决方案1】:

    在文章 The Trouble with Checked Exceptions 和 Anders Hejlsberg(C# 语言的设计者)自己的声音中,C# 不支持检查异常的三个主要原因,因为它们在 Java 中被发现和验证:

    • 对检查的异常中立

      “C#对checked基本上是沉默的 异常问题。一旦更好 解决方案是已知的——相信我,我们 继续想——我们可以走了 回来,实际上把东西放进去 地方。”

    • 带有检查异常的版本控制

      “向 throws 添加新异常 新版本中的子句破坏了客户端 代码。这就像在一个方法中添加一个方法 界面。发布后 界面,实用 目的不变,……”

      “有趣的是,人们认为 关于异常的重要一点是 处理它们。那不是 关于异常的重要事情。在一个 写得很好的应用程序有一个 十比一,在我看来, 最后尝试尝试捕捉。或者在 C# 中, using 语句,它们是 最后试试。”

    • 已检查异常的可扩展性

      “在小的,检查的异常是 很诱人……麻烦 当您开始建造大型建筑时开始 您正在与四个人交谈的系统 或五个不同的子系统。每个 子系统抛出四到十 例外。现在,每次你走上去 聚合阶梯,你有 你下方的这个指数层次结构 您必须处理的异常。 你最终不得不申报 40 你可能抛出的异常...... 它只是气球失控了。”

    在他的文章“Why doesn't C# have exception specifications?”中,Anson Horton(Visual C# 程序经理)也列出了以下原因(每个点的详细信息请参见文章):

    • 版本控制
    • 生产力和代码质量
    • 让班级作者区分的不切实际 选中未选中异常
    • 难以确定接口的正确异常。

    有趣的是,尽管如此,C# 确实支持通过<exception> 标记对给定方法引发的异常进行记录,编译器甚至不厌其烦地验证所引用的异常类型是否确实存在。但是,没有检查调用站点或方法的使用情况。

    您可能还想查看Exception Hunter,这是Red Gate Software 的一个商业工具,它使用静态分析来确定和报告方法抛出的异常并且可能未被捕获:

    Exception Hunter 是一个新的分析 查找和报告集合的工具 您的功能可能的例外情况 可能会在您发货之前抛出。 有了它,你可以找到未处理的 异常容易和快速,低至 抛出的代码行 例外。拿到结果后, 您可以决定需要哪些例外 待处理(有一些例外 处理代码)在你发布你的 应用到野外。

    最后,Bruce EckelThinking in Java 的作者,有一篇名为“Does Java need Checked Exceptions?”的文章,可能也值得一读,因为为什么 C# 中不存在已检查异常的问题通常扎根于与 Java 的比较。

    【讨论】:

    • 我完全支持带有检查异常的版本控制带有检查异常的版本控制点。
    • 有趣的是,根据我的经验,许多开发人员倾向于忽略“一旦知道更好的解决方案”部分。现在是 2015 年,在该声明发布将近 12 年之后,我还没有看到 C# 或任何其他语言(如 Kotlin 等)中的任何“更好的解决方案”(或者我错过了它?我现在不再使用 C# 编程了。)而且只是在没有更好解决方案的情况下删除检查的异常会使想法更糟,恕我直言,而不是更好。在进行了一些重构以验证异常是否在所有情况下都得到正确处理后,我们在其中一个 C# 应用程序中遇到了糟糕的情况,因为编译器不会告诉我们。
    • 使用版本控制,除非绝对必要,否则答案就是不要在新版本中抛出检查异常。我不同意并说 Java 对检查的异常保持沉默,而 c# 是固执己见的。 IE。 Java 允许您抛出已检查异常或运行时异常,但由您决定,而 c# 则强制您抛出运行时异常。听起来他的论点归结为“已检查的异常被滥用,所以我们不会让你使用它们”。
    • 如果 IDE 或编译器对程序员未处理的异常产生警告,它会使软件更加健壮并减少运行时错误。
    【解决方案2】:

    因为对已检查异常的响应几乎总是:

    try {
      // exception throwing code
    } catch(Exception e) {
       // either
       log.error("Error fooing bar",e);
       // OR
       throw new RuntimeException(e);
    }
    

    如果你真的知道在抛出特定异常时可以做些什么,那么你可以捕获它然后处理它,否则它只是安抚编译器的咒语。

    【讨论】:

    • 但实际上,不应该这样。很多例外都不是致命的。主要问题是 Checked 可能不应该是默认的异常类型,并且它们在 Java 中被过度使用。但是检查的异常仍然很有用。
    • 如果程序员是懒惰的,那是Java的错吗?如果程序员过度使用它们,那就是糟糕的 API 设计,再说一遍,Java 的错在哪里?在我的书中,向后弯曲语言以安抚懒惰或愚蠢从来都不是一个好主意。我,一方面,相信检查异常是一个有用的工具,仅此而已
    • @mcjabberz JDK 过度使用它们。那是 Java 的错。
    • Java 充满了教条。
    • 如果我没有在我的代码中捕获异常,我希望 C# 创建一个警告。这将使生活变得更加轻松。
    【解决方案3】:

    C# 的基本设计理念是实际捕获异常很少有用,而在异常情况下清理资源非常重要。我认为可以公平地说using(IDisposable 模式)是他们对检查异常的回答。请参阅 [1] 了解更多信息。

    1. http://www.artima.com/intv/handcuffs.html

    【讨论】:

      【解决方案4】:

      在设计 .NET 时,Java 已经检查异常有一段时间了,Java 开发人员最多将这个功能视为 controversial controversial。因此 .NET 设计人员chose 不要将其包含在 C# 语言中。

      【讨论】:

      • 看起来很有趣,但我认为这是事实,也是 C# 设计语言团队永远不会告诉我们的主要原因之一 :)
      • 看来 andersnoras.com 链接已损坏
      【解决方案5】:

      从根本上说,是否应该处理异常是调用者的属性,而不是函数的属性。

      例如,在某些程序中,处理 IOException 没有任何价值(考虑临时命令行实用程序来执行数据处理;它们永远不会被“用户”使用,它们是使用的专业工具由专业人员)。在某些程序中,在调用“附近”的某个点处理 IOException 是有价值的(也许如果您为配置文件获取 FNFE,您将退回到一些默认值,或者查看另一个位置,或者类似的东西自然)。在其他程序中,您希望它在处理之前很久就冒泡(例如,您可能希望它在到达 UI 之前中止,此时它应该提醒用户出现问题。

      每种情况都依赖于应用程序,而不是。然而,除了经过检查的异常,它是 library 做出决定的。 Java IO 库决定将使用已检查异常(强烈建议在调用本地处理),而在某些程序中,更好的策略可能是非本地处理或根本不处理。

      这显示了在实践中检查异常的真正缺陷,它远比表面的(尽管也很重要)缺陷更为根本,太多的人会编写愚蠢的异常处理程序来让编译器闭嘴。我描述的问题即使在经验丰富、尽职尽责的开发人员编写程序时也是一个问题。

      【讨论】:

      • +1。 Checked-ness 应该是调用站点和捕获站点之间关系的函数。许多方法指定在发生特定的意外情况时使用某些异常来代替返回。例如,IDictionary.Add() 指定如果密钥已存在,则实现应抛出 ArgumentException。不幸的是,没有办法将因此引发的异常与ArgumentException 区分开来,ArgumentException 是在未预料到的情况下由Dictionary.Add() 实现调用的方法引发的。
      • 如果我要设计一个异常系统,我会有一些离散的(可能是密封的)系统定义的异常类型,但不使用异常类型作为决定捕获什么的主要手段。相反,我会让异常对象包装抽象ExceptionInfo 类的一个或多个对象。 IDictionary.Add 抛出的异常可能一开始是一个 ExpectedException 持有一个 DuplicateKeyExceptionInfo,但如果它渗透到一个意想不到的层,它将变成一个持有同一对象的 UnexpectedException。跨度>
      • 检查的异常可能会被滥用,但我会反驳说,很难知道 C# 中的函数抛出了哪些异常。诱使开发人员执行 catch(Exception)... 这也是一种罪过。如果 c# 或 Visualstudio 强制执行一种方法来查看可能引发哪些异常,那么我同意未检查是要走的路。
      【解决方案6】:

      有趣的是,Microsoft Research 的人员已将检查异常添加到 Spec#,这是他们的 C# 超集。

      【讨论】:

      【解决方案7】:

      安德斯本人在软件工程广播播客的this episode 中回答了这个问题

      【讨论】:

        【解决方案8】:

        因为工作变动,我从 Java 转到了 C#。起初,我有点担心这种差异,但实际上并没有产生任何影响。

        可能是因为我来自C++,有异常声明,但不常用。我编写每一行代码,就好像它可以抛出一样——总是在 Disposable 周围使用 using,并考虑我应该在 finally 中进行清理。

        回想起来,Java 中 throws 声明的传播并没有真正让我得到任何东西。

        我想用一种方式说函数绝对不会抛出 - 我认为这样会更有用。

        【讨论】:

        • 函数可以在一定程度上肯定永远不会抛出 - 例如,内存不足的情况几乎可以在任何地方导致 OutOfMemoryException。也许一个函数可以声称它永远不会“故意”抛出 - 但这有多大用处?你可以用这样的声明做什么有用的?
        【解决方案9】:

        除了已经编写的响应之外,没有检查异常在许多情况下对您有很大帮助。检查异常使泛型更难实现,如果您阅读了闭包提案,您会注意到每个闭包提案都必须以一种相当丑陋的方式解决检查异常。

        【讨论】:

          【解决方案10】:

          我有时会错过 C#/.NET 中的已检查异常。

          我想除了 Java 之外没有其他著名的平台拥有它们。也许 .NET 的人只是顺其自然……

          【讨论】:

          • 问题是很少有程序员真正像设计的那样使用检查异常。这是一个理想与实践的案例。理想的检查异常非常有用,但在实践中它们大多为程序员增加了额外的工作。
          • 遵循理想的实践需要理想的程序员。
          • @Constantin: ...或者API设计中的一两个类
          猜你喜欢
          • 2010-11-05
          • 1970-01-01
          • 2012-07-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多