检查异常的优缺点是什么?

时间:2009-05-26 21:55:25

标签: c# java exception

您更喜欢Java中的已检查异常处理或C#中未经检查的异常处理以及为什么?

10 个答案:

答案 0 :(得分:16)

咩。

如果使用得当,检查异常是一件好事,但通常情况下会导致以下内容:

doSomething();
try
{
  somethingThrowsCheckedException();
}
catch(ThatCheckedException)
{ }
doSomethingElse();    

坦率地说,那是错的。你应该让你不处理的异常冒泡。

使用正确的已检查例外情况可能会很好。但是很常见,正确执行检查异常的结果是这样的方法签名:

public void itMightThrow() throws Exception1, Exception2, Exception3, Exception4, // ...
Exception12, Exception13, /* ... */ Exception4499379874
{
  // body
}
我夸大了吗?只是轻微。

修改

也就是说,在异常处理方面,我更喜欢C#而不是Java,这与检查异常无关(如果我使用Spec#,我可以得到它)。不,我喜欢的是当你抛出异常时,而不是当你实例化一个在Java中的时候,就会填充C#中的堆栈跟踪。

编辑2:这是评论者@Yishai,@ Eddie,@ Bill K:

首先,您应该查看this thread以获取有关如何在不实例化异常的情况下获取堆栈跟踪的信息。请记住,走堆栈是一个繁重的过程,不应该定期进行。

其次,原因我喜欢在throwal而不是在实例化时填充C#的异常堆栈跟踪,你可以这样做:

private MyException NewException(string message)
{
   MyException e = new MyException(message);
   Logger.LogException(message, e);
   return e;
}

// and elsewhere...
if(mustThrow)
{
   throw NewException("WHOOOOPSIEE!");
}

如果没有堆栈跟踪中包含的NewException方法,这是Java无法做到的技巧。

答案 1 :(得分:15)

我认为已检查的例外是一项失败的实验。异常的最初目标是消除验证每个函数调用返回的需要,这导致程序programs that are difficult to read, and probably inefficient as well, thus discouraging programmers from signaling and handling exceptions.

虽然在纸面上很棒,但在实践中,已检查的异常重新引入了同样的问题异常应该首先消除。它们在应用程序层之间添加了紧密耦合。它们使库无法在后续版本中更改其实现。 link posted by crausher详细介绍了问题,并且比以往任何时候都更好地解释了这些问题。

答案 2 :(得分:11)

我更喜欢检查过的例外情况,因为那些可能出错的事情是你无法提前预测的。例如,IOException或SQLException。它告诉程序员他们必须考虑到一些不可预测的错误,他们不能写出不会抛出异常的强大代码,无论他们尝试多少。

程序员太多次将已检查的异常视为要处理的语言事物。它不是(或者不会在设计良好的API中) - 它表明操作中存在不可预测的行为,并且您应该依赖于操作的确定性结果,在相同输入的情况下始终工作相同

话虽如此,实际上检查过的例外有两件事:

  1. 并非所有用Java编写的应用程序都需要这种健壮性。 用于关闭已检查异常的编译器级别标志会很好 - 尽管这会导致API滥用已检查的异常,当他们的开发人员使用标志设置将其关闭时。在考虑更好的comprimise之后,我目前的想法是编译器警告是这里最好的平衡。如果检查的异常是编译器警告,包括编译器警告,如果忽略了几个层(因此忽略了一个将被编译到类中的事实),这样调用者至少知道捕获异常,即使他不能知道哪一个,然后那些不关心的人会忽略编译器的警告,而那些做不了的人,没有任何人被迫编写错误处理代码,他们不关心让他们的代码进行编译。
  2. 异常链接花了太长时间(版本1.4)来介绍。缺乏异常链条导致很多坏习惯早期发展,而不是每个人都在做:

    抛出新的RuntimeException(e);

  3. 当他们不知道该怎么做时。

    此外,检查异常是另一个可能出错的API设计元素,API的用户必须承受设计缺陷。

    编辑:另一个答案指出两个问题促使C#设计决定没有检查异常。在我看来,这两个论点都非常糟糕,所以我认为它们值得解决/平衡。

    1. 版本。争论的焦点是,如果您更改了API实现并希望添加其他已检查的异常,那么您将破坏现有的客户端代码。
    2. Scallability。在您知道它之前,您有一个方法可以抛出15个已检查的异常。
    3. 我认为这两个版本都受到了无关紧要的一点,即当这些评论被提出时,已经接受了处理检查异常的正确方法,即向下一级进行处理将包含一个适合于抽象的不同的已检查异常。您的API。例如,如果您有一个可以处理IOException,SQLException或XML相关异常的存储API,那么正确设计的API会隐藏一般PersistanceException背后的差异或类似的东西。

      除了一般的设计指导外,具体而言,论证确实导致了很多关于替代方案的问题:

      1. 版本。因此,开发人员针对您的数据库API开发,认为他们捕获并处理了相关的异常(比如DatabaseException),然后您决定在下一个版本中添加NetworkException以捕获与数据库的网络级通信问题。现在你刚刚打破了现有代码的所有兼容性,编译器甚至都不会抱怨它。如果幸运的话,每个人都可以在回归测试中发现它。
      2. 可扩展性。在C#解决方案中,如果有三个API级别可能会访问volatile资源,那么您完全依赖于API文档,因为编译器不会告诉您。
      3. 对于网络应用程序来说,这是一个很棒的设计,在这个应用程序中,死亡并向用户显示一个很好的错误500页是关于所有人都在做的事情(因为交易是由容器处理的)。但并非所有应用程序都考虑到这些要求。

        争论最终沸腾了(无论如何):不要担心异常,任何事情都可能出错,只是建立一个全能的。

        行。这是已检查和未检查的异常方法之间的核心差异。已检查的异常会向程序员发出无法预测的不可预测的呼叫。未经检查的异常方法只是假设所有错误条件都属于同一个类,它们只是具有不同的名称,并且它们未经检查,因此没有人可以捕获它们。

        现在这些论点在CLR级别上确实有用。我同意所有已检查的异常应该在编译器级别,而不是运行时级别。

答案 3 :(得分:7)

我从未使用过Java,但自从我读完

我很确定我不喜欢已检查的异常(在当前的实现中)。

提到的两个要点如下:

<强> Versionability

  

Anders Hejlsberg:让我们从版本开始,因为问题很容易在那里看到。假设我创建了一个声明它抛出异常A,B和C的方法foo。在foo的第二个版本中,我想添加一些功能,现在foo可能会抛出异常D.这对我来说是一个彻底的改变。将D添加到该方法的throws子句中,因为该方法的现有调用者几乎肯定不会处理该异常。

     

在新版本的throws子句中添加新的异常会破坏客户端代码。这就像在界面中添加方法一样。在发布接口之后,它实际上是不可变的,因为它的任何实现都可能具有您要在下一个版本中添加的方法。所以你必须创建一个新的界面。与异常类似,你要么必须创建一个名为foo2的全新方法,它抛出更多异常,要么必须在新foo中捕获异常D,并将D转换为A,B或C.

<强>可扩展性

  

Anders Hejlsberg:可扩展性问题与可版本性问题有些相关。在小的,经过检查的例外中非常诱人。举个小例子,你可以证明你实际上已经检查过你发现了FileNotFoundException,并不是很好吗?好吧,当你只是调用一个API时,这很好。当您开始构建与四个或五个不同子系统通信的大型系统时,麻烦就开始了。每个子系统抛出四到十个异常。现在,每当你走上聚合的阶梯时,你就有了这个指数层次结构,在你必须处理的异常之下。您最终必须声明可能抛出的40个异常。一旦你将它与另一个子系统聚合,你的throws子句中就有80个例外。它只是气球失控。

     

在大型的,经检查的异常变得如此恼怒,人们完全绕过这个特征。他们要么说,“抛出异常”,到处都是;或者 - 我不能告诉你我见过多少次 - 他们说,“试着,哒哒哒哒哒,卷曲的卷曲。”他们认为,“我以后会回来处理这些空的捕获条款”,然后他们当然不会这样做。在这些情况下,已检查的异常实际上降低了大型系统的质量。

答案 4 :(得分:5)

好的,我不打算回复,但这需要很长时间才能关闭并在围栏的一侧得到很多答案,所以我觉得有必要在另一边权衡。

我支持检查异常 - 正确使用时 - 并且相信它们是一件好事。我上面已经多次听过所有的论点,并且在一些反对检查异常的论据中有一些优点。但在网上,我认为他们是积极的。使用C#和Java进行编程,我发现C#程序更难以对Exceptions进行稳定。检查异常的好处是JavaDoc 保证告诉您可以从该方法抛出异常。使用C#,您依赖编码器记住告诉您可以从任何给定方法抛出哪些异常,以及可能从该方法调用的任何方法抛出哪些异常,等等。

如果你想创建5-9的可靠代码,你需要知道你可以从你调用的代码抛出什么异常,这样你就可以推断出什么可以从中恢复,什么必须导致你放弃你在做什么。如果是C#,你可以这样做,但它会涉及大量的反复试验,直到你看到所有可能抛出的异常。或者你只是抓住了Exception并尽力而为。

这两种方法都有利有弊,即Java和C#。合理的论据可以有利于两者,也有利于两者。同样,在网上,我更喜欢Java选择的方法,但是我今天要重新编写Java,我会更改API以将一些已检查的异常更改为运行时异常。 Java API在使用已检查的异常时不一致。正如其他人所说,Exception链接花了很长时间才能成为标准API功能和JVM的一部分。

然而,经过检查的例外情况下的指控往往属于“懒惰的程序员滥用这种语言特征”的范畴。确实如此。但是许多语言及其功能都是如此。 “懒惰的程序员”论证是一个弱点。

让我们来解决那些不属于“懒惰程序员”的主要抱怨:

  1. 可版本性 - 是的,在新版本的代码中抛出一个新的Exception会破坏盲目放入新JAR文件的客户端的编译。 IMO,这是一个的事情(只要你有充分的理由抛出额外的检查异常),因为你的图书馆的客户必须推理他们的内容需要做这个行为改变。如果一切都未经检查,那么您的客户不一定有任何线索(直到发生异常)您的行为已经改变。如果您要更改代码的行为,那么您的客户必须了解这一点是合理的。你有没有更新到第三方库的新版本只是为了发现它的行为已经无形改变,现在你的程序被破坏了?如果您在库中进行了更改行为更改,则使用早期版本的库中断与客户端的自动兼容性。

  2. 可伸缩性 - 如果您通过将已检查的异常转换为适合您的API层的特定已检查(或未经检查)异常来正确处理这些异常,则这将成为无问题。也就是说,如果您正确编码,此问题就会消失。这样做,您可以正确隐藏您的实施细节,无论如何您的呼叫者都不应该关注。

  3. 很多时候,这只是一个与人有关的宗教问题,这就是为什么我(不必要地,我知道)感到恼火。如果您对检查异常有宗教厌恶,那就没问题。如果你有一个针对已检查异常的理由参数,那就没问题。我已经看到了很好的理由(我大多不同意,但仍然......)。但大多数情况下,我看到针对已检查异常的错误论据,包括在讨论Java 1.0时公平合理的参数,但在Java的现代版本中它们不再适用。

答案 5 :(得分:2)

在实践中,最好使用已检查的异常处理,因为当您的应用程序在凌晨2点开始充斥错误日志并且您接到调用以进行一些调试时,它可以提供更详细的信息......

答案 6 :(得分:2)

在我看来,存在检查异常是合适的情况。 Java中可能有不同的功能可以更好地支持它们。它并非没有困难(例如,在某些情况下,您可能需要检查异常,而在其他情况下则不需要)。当然,Java也支持未经检查的异常类型。

通常应记录适合检查的异常类型。文档的最佳位置在代码中。民粹主义的做法只是为了解决问题而只考虑幸福的情况。

答案 7 :(得分:0)

只要可检索的异常是可恢复的,或者由于编程错误(例如对ResultSet的索引访问无效),它们就很好。否则,它们会通过强制编码器在许多方法签名中声明类似IOException之类的东西来污染代码层和API,并且对客户端代码没有任何实际用处。

答案 8 :(得分:0)

我认为在大多数情况下,检查异常是浪费时间。它们包含了randolpho提到的反模式或广泛创建自定义异常以将您的实现与已使用的库分离。 摆脱这个“功能”可以让你专注于你想做的事情。

答案 9 :(得分:0)

我唯一喜欢编译器检查的是函数是否抛出异常。可抛出的具体异常无关紧要。问题在于,根据我的经验,有很多函数不会抛出任何东西,如果在函数规范中记录了它会很好。对于这些功能,您不必担心异常处理。