【发布时间】:2008-09-23 21:52:24
【问题描述】:
我知道用谷歌搜索可以找到合适的答案,但我更喜欢听听您的个人(也许是技术)意见。
Java 和 C# 在抛出异常方面的差异的主要原因是什么?
在 Java 中,抛出异常的方法的签名必须使用“throws”关键字,而在 C# 中,您在编译时不知道是否会抛出异常。
【问题讨论】:
我知道用谷歌搜索可以找到合适的答案,但我更喜欢听听您的个人(也许是技术)意见。
Java 和 C# 在抛出异常方面的差异的主要原因是什么?
在 Java 中,抛出异常的方法的签名必须使用“throws”关键字,而在 C# 中,您在编译时不知道是否会抛出异常。
【问题讨论】:
在文章 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 Eckel,Thinking in Java 的作者,有一篇名为“Does Java need Checked Exceptions?”的文章,可能也值得一读,因为为什么 C# 中不存在已检查异常的问题通常扎根于与 Java 的比较。
【讨论】:
因为对已检查异常的响应几乎总是:
try {
// exception throwing code
} catch(Exception e) {
// either
log.error("Error fooing bar",e);
// OR
throw new RuntimeException(e);
}
如果你真的知道在抛出特定异常时可以做些什么,那么你可以捕获它然后处理它,否则它只是安抚编译器的咒语。
【讨论】:
C# 的基本设计理念是实际捕获异常很少有用,而在异常情况下清理资源非常重要。我认为可以公平地说using(IDisposable 模式)是他们对检查异常的回答。请参阅 [1] 了解更多信息。
【讨论】:
在设计 .NET 时,Java 已经检查异常有一段时间了,Java 开发人员最多将这个功能视为 controversial controversial。因此 .NET 设计人员chose 不要将其包含在 C# 语言中。
【讨论】:
从根本上说,是否应该处理异常是调用者的属性,而不是函数的属性。
例如,在某些程序中,处理 IOException 没有任何价值(考虑临时命令行实用程序来执行数据处理;它们永远不会被“用户”使用,它们是使用的专业工具由专业人员)。在某些程序中,在调用“附近”的某个点处理 IOException 是有价值的(也许如果您为配置文件获取 FNFE,您将退回到一些默认值,或者查看另一个位置,或者类似的东西自然)。在其他程序中,您希望它在处理之前很久就冒泡(例如,您可能希望它在到达 UI 之前中止,此时它应该提醒用户出现问题。
每种情况都依赖于应用程序,而不是库。然而,除了经过检查的异常,它是 library 做出决定的。 Java IO 库决定将使用已检查异常(强烈建议在调用本地处理),而在某些程序中,更好的策略可能是非本地处理或根本不处理。
这显示了在实践中检查异常的真正缺陷,它远比表面的(尽管也很重要)缺陷更为根本,太多的人会编写愚蠢的异常处理程序来让编译器闭嘴。我描述的问题即使在经验丰富、尽职尽责的开发人员编写程序时也是一个问题。
【讨论】:
IDictionary.Add() 指定如果密钥已存在,则实现应抛出 ArgumentException。不幸的是,没有办法将因此引发的异常与ArgumentException 区分开来,ArgumentException 是在未预料到的情况下由Dictionary.Add() 实现调用的方法引发的。
ExceptionInfo 类的一个或多个对象。 IDictionary.Add 抛出的异常可能一开始是一个 ExpectedException 持有一个 DuplicateKeyExceptionInfo,但如果它渗透到一个意想不到的层,它将变成一个持有同一对象的 UnexpectedException。跨度>
有趣的是,Microsoft Research 的人员已将检查异常添加到 Spec#,这是他们的 C# 超集。
【讨论】:
安德斯本人在软件工程广播播客的this episode 中回答了这个问题
【讨论】:
因为工作变动,我从 Java 转到了 C#。起初,我有点担心这种差异,但实际上并没有产生任何影响。
可能是因为我来自C++,有异常声明,但不常用。我编写每一行代码,就好像它可以抛出一样——总是在 Disposable 周围使用 using,并考虑我应该在 finally 中进行清理。
回想起来,Java 中 throws 声明的传播并没有真正让我得到任何东西。
我想用一种方式说函数绝对不会抛出 - 我认为这样会更有用。
【讨论】:
除了已经编写的响应之外,没有检查异常在许多情况下对您有很大帮助。检查异常使泛型更难实现,如果您阅读了闭包提案,您会注意到每个闭包提案都必须以一种相当丑陋的方式解决检查异常。
【讨论】:
我有时会错过 C#/.NET 中的已检查异常。
我想除了 Java 之外没有其他著名的平台拥有它们。也许 .NET 的人只是顺其自然……
【讨论】: