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

时间:2022-10-17 17:10:08

Do you prefer checked exception handling like in Java or unchecked exception handling like in C# and why?

您更喜欢在Java中那样的检查异常处理,还是在c#中那样的未检查异常处理,为什么?

10 个解决方案

#1


16  

Meh.

咩。

Checked exceptions are a great thing when used properly, but more often than not they lead to stuff like:

检查异常在正确使用时是一件很好的事情,但它们往往导致以下情况:

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

And, frankly, that's just wrong. You should let exceptions you don't handle bubble up.

坦白地说,这是错误的。你应该让你无法处理的异常出现。

Checked exceptions used properly can be good. But very frequently, the result of doing checked exceptions properly is method signatures like this:

正确使用的检查异常可能是好的。但是非常频繁地,检查异常的结果是这样的方法签名:

public void itMightThrow() throws Exception1, Exception2, Exception3, Exception4, // ...
Exception12, Exception13, /* ... */ Exception4499379874
{
  // body
}

Am I exaggerating? Only slightly.

我夸大?仅略。

Edit:

编辑:

That said, one thing I prefer about C# over Java when it comes to exception handling has nothing to do with checked exceptions (I can get that if I go with Spec# anyway). No, what I like is that the stack trace in C# is populated when you throw an exception, rather than when you instantiate one as it is in Java.

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

Edit 2: This is for the commenters @Yishai, @Eddie, @Bill K:

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

First, you should check out this thread for information on how to get a stack trace without instantiating an exception. Keep in mind that walking the stack is a heavy process and should not be done on a regular basis.

首先,您应该检查这个线程,了解如何在不实例化异常的情况下获得堆栈跟踪。记住,在堆栈中行走是一个很重的过程,不应该经常进行。

Second, the reason I like C#'s exception stack trace being populated at throwal rather than at instantiation is that you can do things like this:

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

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

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

That's a trick you can't do in Java without having the NewException method included in the stack trace.

在Java中,如果不将NewException方法包含在堆栈跟踪中,就无法实现这个技巧。

#2


15  

I think checked exceptions are a failed experiment. The original goal of exception was to eliminate the need to validate every function call return, which was resulting in programs programs that are difficult to read, and probably inefficient as well, thus discouraging programmers from signaling and handling exceptions.

我认为检查异常是一个失败的实验。异常的最初目标是消除验证每个函数调用返回的需要,这导致程序难以读取,而且可能效率也很低,从而阻止程序员发送信号和处理异常。

While great on paper, in practice the checked exceptions reintroduced the very same problem exception were supposed to eliminate in the first place. They add a tightly coupling between the layers of application. They make it impossible for libraries to change their implementation in subsequent versions. The link posted by crausher goes into details and explain the problems much better than I ever could.

虽然理论上很好,但在实践中,检查异常重新引入的问题异常应该首先被排除。它们在应用程序层之间添加了紧密耦合。它们使库不可能在后续版本中更改其实现。crausher发布的链接详细地解释了问题,比我能解释的更好。

#3


11  

I prefer checked exceptions for things that can go wrong that you cannot predict ahead of time. For example, IOException, or SQLException. It tells the programmer that they have to account for something unpredictable going wrong, they cannot write robust code that will not throw an exception, no matter how much they try.

我更喜欢检查异常,以防出现您无法提前预测的错误。例如,IOException或SQLException。它告诉程序员,他们必须考虑一些不可预测的错误,他们不能编写健壮的代码,无论他们多么努力,也不会抛出异常。

Too many times programmers view a checked exception as a language thing to handle. It isn't (or won't be in a well designed API) - it is an indication that there is unpredictable behavior inherent in the operation, and you should rely on a deterministic result of the operation always working the same given the same inputs.

很多时候程序员把检查过的异常看作是一种需要处理的语言。它不是(或者不会在设计良好的API中)——它表明操作中存在不可预知的行为,并且您应该依赖于给定相同输入的操作的确定性结果。

That being said, in practice checked exceptions suffered from two things:

也就是说,在实践中被检查的例外有两方面:

  1. Not all applications written in Java need that robustness. A compiler-level flag to turn off checked exceptions would be nice - although that could lead to APIs abusing checked exceptions when their developers work with the flag set to turn them off. After thinking about a better comprimise here, my current thinking is that a compiler warning is the best ballance here. If checked exceptions were compiler warnings, including a compiler warning if one was ignored several layers down (so the fact that one was ignored would be compiled into the class), so that the caller would at least know to catch Exception even if he couldn't know which one, then those who don't care would ignore the compiler warning, and those who do would not, without anyone being forced to write error handling code they don't care about to get their code to compile.
  2. 不是所有用Java编写的应用程序都需要这种健壮性。编译器层国旗关掉受控异常就好了——虽然这可能会导致api滥用受控异常当他们的开发人员使用国旗后将关闭它们。思考更好的comprimise这里,我目前的想法是,编译器警告是最好的余额。如果编译器警告已检查的异常,包括编译器警告如果一个是忽略了几层(所以这一事实将被忽略,编译成类),所以调用者至少会知道捕获异常,即使他不知道哪一个,那么那些不在乎会忽略编译器警告,和那些不会,没有人*编写错误处理代码他们不关心他们的代码编译。
  3. Exception chaining took much too long (version 1.4) to introduce. The lack of exception chaining caused a lot of bad habits to develop early, instead of everyone just doing:

    异常链接使用的时间太长(版本1.4)。缺乏异常链接导致许多坏习惯提前形成,而不是每个人都这么做:

    throw new RuntimeException(e);

    把新的RuntimeException(e);

when they didn't know what to do.

当他们不知道该做什么时。

Also, a checked exception is another API design element to potentially get wrong, and the users of the API have to suffer with the design flaw.

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

EDIT: Another answer points to two issues that motivated the C# design decision of no checked exceptions. To my mind, both of those arguments are very bad, so I thought they were worth addressing/counter balancing.

编辑:另一个答案指出了两个问题,它们促使c#设计决定不检查异常。在我看来,这两种观点都很糟糕,所以我认为它们值得讨论/权衡。

  1. Versioning. The argument is that if you change your API implementation and want to add additional checked exceptions, then you break existing client code.
  2. 版本控制。其理由是,如果您更改API实现并希望添加额外的检查异常,那么您将破坏现有的客户端代码。
  3. Scallability. Before you know it you have a method that throws 15 checked exceptions.
  4. Scallability。在您知道它之前,您有一个抛出15个已检查异常的方法。

I think both versions suffer from the unaddressed point that when those remarks were made it was already accepted that the proper way to deal with a checked exception moving up to the next level would be by wrapping a different checked exception appropriate to the abstraction of your API. For example, if you have a storage API that could deal with IOException, SQLException, or XML related exceptions, a properly desgined API would hide those differences behind a general PersistanceException or something like that.

我认为这两个版本都有一个未被提及的问题,即当这些评论发表时,人们已经接受了处理下一个级别的检查异常的正确方法是包装一个与API的抽象相适应的检查异常。例如,如果您有一个可以处理IOException、SQLException或与XML相关的异常的存储API,那么一个经过适当解压的API会将这些差异隐藏在一般的persistence异常或类似的异常后面。

Besides that general design guidance, in the specific the arguments really lead to a lot of questions about the alternatives:

除了一般的设计指导,在具体的争论中,真的导致了很多关于替代方案的问题:

  1. Versioning. So a developer developed against your database API, thinking that they caught and handled the relevant exceptions (say DatabaseException) and then you decide in the next version to add a NetworkException to capture network level communication issues with the database. Now you just broke all compatability with existing code, and the compiler won't even complain about it. Everyone gets to discover it in regression testing, if they are lucky.
  2. 版本控制。因此,开发人员根据您的数据库API开发,认为他们捕获并处理了相关的异常(比如DatabaseException),然后您决定在下一个版本中添加NetworkException以捕获与数据库的网络级别通信问题。现在,您刚刚破坏了所有与现有代码的兼容性,编译器甚至不会抱怨它。如果幸运的话,每个人都可以在回归测试中发现它。
  3. Scalability. In the C# solution, if three API levels down there is a potential for access to a volatile resource, you are relying entirely on the API documentation, because the compiler won't tell you that.
  4. 可伸缩性。在c#解决方案中,如果下面有三个API级别可以访问易失性资源,那么您完全依赖于API文档,因为编译器不会告诉您这些。

That is a great design for web apps where dying and showing the user a nice error 500 page is about all anyone bothers doing (since transactions are handled by the container anyway). But not all applications are built with such requirements in mind.

对于web应用程序来说,这是一个很好的设计,在这个设计中,死亡并向用户显示一个漂亮的错误500页是所有人都在做的事情(因为事务无论如何都是由容器处理的)。但并不是所有的应用程序都是基于这样的需求构建的。

The argument ends up boiling down (to me anyway): Don't worry about exceptions, anything can go wrong and just build a catch-all.

争论最终以沸沸扬扬而告终(对我而言):不要担心例外,任何事情都可能出错,只需要构建一个万能锁。

OK. That is the core difference between a checked and unchecked exception approach. The checked exception alerts the programmer to volatile unpredictable calls. The unchecked exception approach just assumes that all error conditions are of the same class, they just have different names, and they are made unchecked so that no one goes around catching them.

好的。这是选中的和未检查的异常方法之间的核心区别。检查过的异常提醒程序员进行不稳定的、不可预测的调用。未检查异常方法只是假设所有的错误条件都属于同一个类,它们只是有不同的名称,它们被不检查,因此没有人会到处捕捉它们。

Now the arguments do have merit at the CLR level. I agree that all checked exceptions should be at the compiler level, not the runtime level.

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

#4


7  

I have never used Java, but since I read

我从来没有使用过Java,但是自从我读了以后

I am quite sure I don't like checked exceptions (in the current implementation).

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

The two main points mentioned are the following.

提到的两个要点如下。

Versionability

Versionability

Anders Hejlsberg: Let's start with versioning, because the issues are pretty easy to see there. Let's say I create a method foo that declares it throws exceptions A, B, and C. In version two of foo, I want to add a bunch of features, and now foo might throw exception D. It is a breaking change for me to add D to the throws clause of that method, because existing caller of that method will almost certainly not handle that exception.

安德斯·海尔斯伯格:让我们从版本控制开始,因为问题很容易在那里发现。假设我创建一个方法foo,宣称它抛出异常,B和c在foo的两个版本中,我想添加一些功能,和现在foo可能抛出异常D。这对我来说是一个重大改变添加D抛出条款的方法,因为现有的方法的调用者几乎肯定不会处理这个异常。

Adding a new exception to a throws clause in a new version breaks client code. It's like adding a method to an interface. After you publish an interface, it is for all practical purposes immutable, because any implementation of it might have the methods that you want to add in the next version. So you've got to create a new interface instead. Similarly with exceptions, you would either have to create a whole new method called foo2 that throws more exceptions, or you would have to catch exception D in the new foo, and transform the D into an A, B, or C.

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

Scalability

可伸缩性

Anders Hejlsberg: The scalability issue is somewhat related to the versionability issue. In the small, checked exceptions are very enticing. With a little example, you can show that you've actually checked that you caught the FileNotFoundException, and isn't that great? Well, that's fine when you're just calling one API. The trouble begins when you start building big systems where you're talking to four or five different subsystems. Each subsystem throws four to ten exceptions. Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you've got 80 exceptions in your throws clause. It just balloons out of control.

Anders Hejlsberg:可伸缩性问题与版本性问题有关。在小的,受控异常是非常诱人的。通过一个小示例,您可以显示您确实检查了是否捕获了FileNotFoundException,这不是很好吗?当你只调用一个API时,这没问题。当你开始构建大型系统时,麻烦就来了,在这个系统中你要和四五个不同的子系统交流。每个子系统抛出4到10个异常。现在,每次你沿着聚集的阶梯向上走,你就会有一个指数等级在你下面你必须处理的异常。最后必须声明可能抛出的40个异常。一旦你将它与另一个子系统聚合起来在你的抛出子句中有80个异常。它失去了控制。

In the large, checked exceptions become such an irritation that people completely circumvent the feature. They either say, "throws Exception," everywhere; or—and I can't tell you how many times I've seen this—they say, "try, da da da da da, catch curly curly." They think, "Oh I'll come back and deal with these empty catch clauses later," and then of course they never do. In those situations, checked exceptions have actually degraded the quality of the system in the large.

在大型的受控异常中,人们会完全避开这个特性。他们要么说,“抛出例外”,到处都是;或者,我不能告诉你我见过多少次这个——他们说,“试试,大,大,大,大,大,抓住大卷。”他们会想,“哦,我会回来处理这些空的捕捞条款的,”然后他们当然不会这么做。在这些情况下,受控异常实际上降低了系统的质量。

#5


5  

OK, I wasn't going to reply, but this is taking too long to get closed and got many answers on one side of the fence, so I feel the need to weigh in on the other side.

好吧,我本来不想回答的,但是这需要很长时间才能结束,在篱笆的一边有很多答案,所以我觉得有必要在另一边权衡一下。

I support checked exceptions -- when properly used -- and believe that they are a Good Thing. I've heard all of the arguments above many times, and there is some merit in some of the arguments against checked exceptions. But on net, I think they are positive. Having programmed in C# and Java, both, I find C# programs are more difficult to make stable against Exceptions. The great thing about checked exceptions is that the JavaDoc is guaranteed to tell you that the Exceptions can be thrown from that method. With C#, you rely on the coder to remember to tell you what exceptions may be thrown from any given method, and also what exceptions may be thrown from any method called by that method, and so on.

我支持检查异常(如果使用得当),并相信它们是一件好事。我已经听过以上所有的论点很多次了,有些反对检查异常的论点是有价值的。但在网上,我认为他们是积极的。在编写了c#和Java之后,我发现c#程序更难在异常情况下保持稳定。检查异常的好处是,JavaDoc保证告诉您可以从该方法中抛出异常。使用c#,您需要依赖编码器记住可以从任何给定的方法中抛出什么异常,以及可以从该方法调用的任何方法中抛出什么异常,等等。

If you want to create 5-9's reliable code, you need to know what exceptions can be thrown from code that you call, so you can reason about what can be recovered from and what must cause you to abandon what you are doing. If C#, you can do this, but it involves a lot of trial and error until you have seen all of the possible exceptions that can be thrown. Or you just catch Exception and do your best.

如果您想要创建5-9的可靠代码,您需要知道可以从调用的代码中抛出哪些异常,这样您就可以推断可以从哪些代码中恢复,以及哪些代码必须导致您放弃正在做的工作。如果c#,您可以这样做,但是它需要大量的尝试和错误,直到您看到所有可能抛出的异常。或者你只是抓住例外,尽你最大的努力。

There are pros and cons to both approaches, that of Java and C#. Reasoned arguments can be made in favor of both, and against both. Again, on net, I prefer the approach chosen by Java, but were I to re-write Java today, I would change the APIs to change some checked exceptions into runtime exceptions. The Java API is not consistent in its use of checked exceptions. And as someone else said, it took far too long for Exception chaining to appear as a standard API feature and a part of the JVM.

Java和c#这两种方法都有优缺点。理性的论点可以是对两者都有利的,也可以是对两者都不利的。同样,在net上,我更喜欢Java选择的方法,但是如果我今天重写Java,我将更改api,将一些已检查的异常更改为运行时异常。Java API在使用已检查的异常时不一致。正如其他人所说,异常链接作为标准的API特性和JVM的一部分出现的时间太长了。

However, the charges that are lain at the feet of checked exceptions too often fall into the category of, "lazy programmers misuse this language feature." That's true. But that's true of many languages and their features. The "lazy programmer" argument is a weak one.

然而,在检查异常的脚边的指控常常属于“懒惰的程序员误用了这个语言特性”的范畴。这是真的。但是,许多语言和它们的特性都是如此。“懒惰的程序员”的论点很弱。

Let's address the main complaints that don't fall into the "lazy programmer" bucket:

让我们来谈谈不属于“懒惰程序员”范畴的主要抱怨:

  1. Versionability - yes, throwing a new Exception in a new version of your code will break compilation for clients who blindly drop in your new JAR file. IMO, this is a good thing (as long as you have a good reason for throwing an additional checked exception), because the clients of your library have to reason about what they need to do with this behavior change. If everything is unchecked, then your clients don't necessarily have any clue (until an Exception occurs) that your behavior has changed. If you are changing the behavior of your code, then it's reasonable for your clients to have to know about this. Have you ever updated to a new version of a 3rd party library only to find its behavior has invisibly changed and now your program is broken? If you make a breaking behavior change in your library, you should break automatic compatibility with clients using earlier versions of your library.

    版本性——是的,在代码的新版本中抛出一个新异常将破坏对盲目地插入新JAR文件的客户机的编译。在我看来,这是一件好事(只要您有一个抛出额外检查异常的好理由),因为您的库的客户必须考虑如何处理这种行为更改。如果一切都未检查,那么您的客户端并不一定知道(除非发生异常)您的行为发生了变化。如果您正在更改代码的行为,那么您的客户必须了解这一点是合理的。您是否曾经更新到一个新版本的第三方库,却发现它的行为已经无形地改变,现在您的程序被破坏了?如果您在库中进行了中断行为更改,那么应该使用库的早期版本来中断与客户端的自动兼容性。

  2. Scalability - If you handle checked exceptions properly by translating them to a specific checked (or unchecked) exception appropriate to your layer of the API, this becomes a non-issue. That is, if you code properly, this problem disappears. And doing so, you properly hide your implementation details, which your callers shouldn't care about anyway.

    可伸缩性——如果您通过将其转换为适合您的API层的特定检查(或未检查)异常来处理检查异常,这将成为一个无关紧要的问题。也就是说,如果你编码正确,这个问题就消失了。这样做,您就适当地隐藏了实现细节,而您的调用者无论如何都不应该关心这些细节。

Too often, this is simply a religious issue with people, and that's why I get (unnecessarily, I know) irritated. If you have a religious aversion to checked exceptions, that's OK. If you have a reasoned argument against checked exceptions, that's OK. I've seen good reasoned arguments (that I mostly don't agree with, but still...). But mostly I see bad arguments against checked exceptions, including arguments which were fair and reasonable when talking about Java 1.0, but which are no longer applicable given modern releases of Java.

很多时候,这只是人们的宗教问题,这就是我为什么(我知道,这是不必要的)生气的原因。如果你对受控异常有宗教厌恶,那没关系。如果您对已检查的异常有一个合理的论点,那就可以了。我看到过很有道理的论点(我大多不同意,但仍然…)但大多数情况下,我看到的都是针对已检查异常的糟糕参数,包括在讨论Java 1.0时公平合理的参数,但在现代Java版本中,这些参数不再适用。

#6


2  

In practice it is better to use checked exception handling as it allows for more detailed information when your app begins flooding error logs at 2AM and you get a call to do some debugging...

在实践中,最好使用检查异常处理,因为当您的应用程序在凌晨2点开始处理错误日志时,您会得到一个调用来进行一些调试…

#7


2  

In my opinion there exist cases where checked exceptions are appropriate. There are probably features that could be done differently in Java to better support them. It isn't without difficulties (for instance, in some situations you might want an exception checked, in others not). Java does, of course, support unchecked exception types as well.

在我看来,有些情况下检查异常是合适的。在Java中可能有一些特性可以用不同的方法来更好地支持它们。这并非没有困难(例如,在某些情况下,您可能希望检查异常,而在其他情况下则不是)。当然,Java也支持未检查的异常类型。

The sort of exceptions that are suitable to be checked should generally be documented. The best place to document is in the code. The populist approach is just to botch it and only consider the happy case.

通常应该记录适合被检查的异常。编写文档的最佳位置是在代码中。民粹主义的做法只是把事情搞糟,只考虑令人满意的情况。

#8


0  

Checked exceptions are great as long as they are recoverable or not due to programming errors like an invalid index acces to a ResultSet. Otherwise they tend to pollute code layers and APIs by forcing the coder to declare things like IOException in many method signatures and giving nothing really useful to the client code.

检查过的异常是很好的,只要它们是可恢复的,或者不是由于编程错误(如无效的索引进入ResultSet)。否则,它们会迫使程序员在许多方法签名中声明IOException之类的东西,并且不会给客户端代码带来真正有用的东西,从而破坏代码层和api。

#9


0  

I think in most cases checked exception are a waste of time. They entrap to things like the antipattern mentioned by randolpho or extensive creation of custom exceptions to decouple your implementation from used libraries. Beeing rid of this 'feature' lets you focus on what you want to do.

我认为在大多数情况下检查异常是浪费时间。它们会捕捉到randolpho提到的反模式或者大量创建自定义异常来将实现从使用的库中分离出来。删除这个“特性”可以让你专注于你想做的事情。

#10


0  

The only thing I'dd like the compiler to check for me is whether a function throws exceptions or not. What specific exceptions that can be thrown doesn't matter. The thing is that in my experience, there are a lot functions that don't throw anything, and it would be nice if this was documented in the function specification. For these functions you don't have to worry about exception handling.

我唯一想要检查的是一个函数是否抛出异常。可以抛出哪些特定的异常并不重要。在我的经验中,有很多函数不会抛出任何东西,如果在函数规范中记录这一点就更好了。对于这些函数,您不必担心异常处理。

#1


16  

Meh.

咩。

Checked exceptions are a great thing when used properly, but more often than not they lead to stuff like:

检查异常在正确使用时是一件很好的事情,但它们往往导致以下情况:

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

And, frankly, that's just wrong. You should let exceptions you don't handle bubble up.

坦白地说,这是错误的。你应该让你无法处理的异常出现。

Checked exceptions used properly can be good. But very frequently, the result of doing checked exceptions properly is method signatures like this:

正确使用的检查异常可能是好的。但是非常频繁地,检查异常的结果是这样的方法签名:

public void itMightThrow() throws Exception1, Exception2, Exception3, Exception4, // ...
Exception12, Exception13, /* ... */ Exception4499379874
{
  // body
}

Am I exaggerating? Only slightly.

我夸大?仅略。

Edit:

编辑:

That said, one thing I prefer about C# over Java when it comes to exception handling has nothing to do with checked exceptions (I can get that if I go with Spec# anyway). No, what I like is that the stack trace in C# is populated when you throw an exception, rather than when you instantiate one as it is in Java.

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

Edit 2: This is for the commenters @Yishai, @Eddie, @Bill K:

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

First, you should check out this thread for information on how to get a stack trace without instantiating an exception. Keep in mind that walking the stack is a heavy process and should not be done on a regular basis.

首先,您应该检查这个线程,了解如何在不实例化异常的情况下获得堆栈跟踪。记住,在堆栈中行走是一个很重的过程,不应该经常进行。

Second, the reason I like C#'s exception stack trace being populated at throwal rather than at instantiation is that you can do things like this:

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

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

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

That's a trick you can't do in Java without having the NewException method included in the stack trace.

在Java中,如果不将NewException方法包含在堆栈跟踪中,就无法实现这个技巧。

#2


15  

I think checked exceptions are a failed experiment. The original goal of exception was to eliminate the need to validate every function call return, which was resulting in programs programs that are difficult to read, and probably inefficient as well, thus discouraging programmers from signaling and handling exceptions.

我认为检查异常是一个失败的实验。异常的最初目标是消除验证每个函数调用返回的需要,这导致程序难以读取,而且可能效率也很低,从而阻止程序员发送信号和处理异常。

While great on paper, in practice the checked exceptions reintroduced the very same problem exception were supposed to eliminate in the first place. They add a tightly coupling between the layers of application. They make it impossible for libraries to change their implementation in subsequent versions. The link posted by crausher goes into details and explain the problems much better than I ever could.

虽然理论上很好,但在实践中,检查异常重新引入的问题异常应该首先被排除。它们在应用程序层之间添加了紧密耦合。它们使库不可能在后续版本中更改其实现。crausher发布的链接详细地解释了问题,比我能解释的更好。

#3


11  

I prefer checked exceptions for things that can go wrong that you cannot predict ahead of time. For example, IOException, or SQLException. It tells the programmer that they have to account for something unpredictable going wrong, they cannot write robust code that will not throw an exception, no matter how much they try.

我更喜欢检查异常,以防出现您无法提前预测的错误。例如,IOException或SQLException。它告诉程序员,他们必须考虑一些不可预测的错误,他们不能编写健壮的代码,无论他们多么努力,也不会抛出异常。

Too many times programmers view a checked exception as a language thing to handle. It isn't (or won't be in a well designed API) - it is an indication that there is unpredictable behavior inherent in the operation, and you should rely on a deterministic result of the operation always working the same given the same inputs.

很多时候程序员把检查过的异常看作是一种需要处理的语言。它不是(或者不会在设计良好的API中)——它表明操作中存在不可预知的行为,并且您应该依赖于给定相同输入的操作的确定性结果。

That being said, in practice checked exceptions suffered from two things:

也就是说,在实践中被检查的例外有两方面:

  1. Not all applications written in Java need that robustness. A compiler-level flag to turn off checked exceptions would be nice - although that could lead to APIs abusing checked exceptions when their developers work with the flag set to turn them off. After thinking about a better comprimise here, my current thinking is that a compiler warning is the best ballance here. If checked exceptions were compiler warnings, including a compiler warning if one was ignored several layers down (so the fact that one was ignored would be compiled into the class), so that the caller would at least know to catch Exception even if he couldn't know which one, then those who don't care would ignore the compiler warning, and those who do would not, without anyone being forced to write error handling code they don't care about to get their code to compile.
  2. 不是所有用Java编写的应用程序都需要这种健壮性。编译器层国旗关掉受控异常就好了——虽然这可能会导致api滥用受控异常当他们的开发人员使用国旗后将关闭它们。思考更好的comprimise这里,我目前的想法是,编译器警告是最好的余额。如果编译器警告已检查的异常,包括编译器警告如果一个是忽略了几层(所以这一事实将被忽略,编译成类),所以调用者至少会知道捕获异常,即使他不知道哪一个,那么那些不在乎会忽略编译器警告,和那些不会,没有人*编写错误处理代码他们不关心他们的代码编译。
  3. Exception chaining took much too long (version 1.4) to introduce. The lack of exception chaining caused a lot of bad habits to develop early, instead of everyone just doing:

    异常链接使用的时间太长(版本1.4)。缺乏异常链接导致许多坏习惯提前形成,而不是每个人都这么做:

    throw new RuntimeException(e);

    把新的RuntimeException(e);

when they didn't know what to do.

当他们不知道该做什么时。

Also, a checked exception is another API design element to potentially get wrong, and the users of the API have to suffer with the design flaw.

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

EDIT: Another answer points to two issues that motivated the C# design decision of no checked exceptions. To my mind, both of those arguments are very bad, so I thought they were worth addressing/counter balancing.

编辑:另一个答案指出了两个问题,它们促使c#设计决定不检查异常。在我看来,这两种观点都很糟糕,所以我认为它们值得讨论/权衡。

  1. Versioning. The argument is that if you change your API implementation and want to add additional checked exceptions, then you break existing client code.
  2. 版本控制。其理由是,如果您更改API实现并希望添加额外的检查异常,那么您将破坏现有的客户端代码。
  3. Scallability. Before you know it you have a method that throws 15 checked exceptions.
  4. Scallability。在您知道它之前,您有一个抛出15个已检查异常的方法。

I think both versions suffer from the unaddressed point that when those remarks were made it was already accepted that the proper way to deal with a checked exception moving up to the next level would be by wrapping a different checked exception appropriate to the abstraction of your API. For example, if you have a storage API that could deal with IOException, SQLException, or XML related exceptions, a properly desgined API would hide those differences behind a general PersistanceException or something like that.

我认为这两个版本都有一个未被提及的问题,即当这些评论发表时,人们已经接受了处理下一个级别的检查异常的正确方法是包装一个与API的抽象相适应的检查异常。例如,如果您有一个可以处理IOException、SQLException或与XML相关的异常的存储API,那么一个经过适当解压的API会将这些差异隐藏在一般的persistence异常或类似的异常后面。

Besides that general design guidance, in the specific the arguments really lead to a lot of questions about the alternatives:

除了一般的设计指导,在具体的争论中,真的导致了很多关于替代方案的问题:

  1. Versioning. So a developer developed against your database API, thinking that they caught and handled the relevant exceptions (say DatabaseException) and then you decide in the next version to add a NetworkException to capture network level communication issues with the database. Now you just broke all compatability with existing code, and the compiler won't even complain about it. Everyone gets to discover it in regression testing, if they are lucky.
  2. 版本控制。因此,开发人员根据您的数据库API开发,认为他们捕获并处理了相关的异常(比如DatabaseException),然后您决定在下一个版本中添加NetworkException以捕获与数据库的网络级别通信问题。现在,您刚刚破坏了所有与现有代码的兼容性,编译器甚至不会抱怨它。如果幸运的话,每个人都可以在回归测试中发现它。
  3. Scalability. In the C# solution, if three API levels down there is a potential for access to a volatile resource, you are relying entirely on the API documentation, because the compiler won't tell you that.
  4. 可伸缩性。在c#解决方案中,如果下面有三个API级别可以访问易失性资源,那么您完全依赖于API文档,因为编译器不会告诉您这些。

That is a great design for web apps where dying and showing the user a nice error 500 page is about all anyone bothers doing (since transactions are handled by the container anyway). But not all applications are built with such requirements in mind.

对于web应用程序来说,这是一个很好的设计,在这个设计中,死亡并向用户显示一个漂亮的错误500页是所有人都在做的事情(因为事务无论如何都是由容器处理的)。但并不是所有的应用程序都是基于这样的需求构建的。

The argument ends up boiling down (to me anyway): Don't worry about exceptions, anything can go wrong and just build a catch-all.

争论最终以沸沸扬扬而告终(对我而言):不要担心例外,任何事情都可能出错,只需要构建一个万能锁。

OK. That is the core difference between a checked and unchecked exception approach. The checked exception alerts the programmer to volatile unpredictable calls. The unchecked exception approach just assumes that all error conditions are of the same class, they just have different names, and they are made unchecked so that no one goes around catching them.

好的。这是选中的和未检查的异常方法之间的核心区别。检查过的异常提醒程序员进行不稳定的、不可预测的调用。未检查异常方法只是假设所有的错误条件都属于同一个类,它们只是有不同的名称,它们被不检查,因此没有人会到处捕捉它们。

Now the arguments do have merit at the CLR level. I agree that all checked exceptions should be at the compiler level, not the runtime level.

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

#4


7  

I have never used Java, but since I read

我从来没有使用过Java,但是自从我读了以后

I am quite sure I don't like checked exceptions (in the current implementation).

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

The two main points mentioned are the following.

提到的两个要点如下。

Versionability

Versionability

Anders Hejlsberg: Let's start with versioning, because the issues are pretty easy to see there. Let's say I create a method foo that declares it throws exceptions A, B, and C. In version two of foo, I want to add a bunch of features, and now foo might throw exception D. It is a breaking change for me to add D to the throws clause of that method, because existing caller of that method will almost certainly not handle that exception.

安德斯·海尔斯伯格:让我们从版本控制开始,因为问题很容易在那里发现。假设我创建一个方法foo,宣称它抛出异常,B和c在foo的两个版本中,我想添加一些功能,和现在foo可能抛出异常D。这对我来说是一个重大改变添加D抛出条款的方法,因为现有的方法的调用者几乎肯定不会处理这个异常。

Adding a new exception to a throws clause in a new version breaks client code. It's like adding a method to an interface. After you publish an interface, it is for all practical purposes immutable, because any implementation of it might have the methods that you want to add in the next version. So you've got to create a new interface instead. Similarly with exceptions, you would either have to create a whole new method called foo2 that throws more exceptions, or you would have to catch exception D in the new foo, and transform the D into an A, B, or C.

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

Scalability

可伸缩性

Anders Hejlsberg: The scalability issue is somewhat related to the versionability issue. In the small, checked exceptions are very enticing. With a little example, you can show that you've actually checked that you caught the FileNotFoundException, and isn't that great? Well, that's fine when you're just calling one API. The trouble begins when you start building big systems where you're talking to four or five different subsystems. Each subsystem throws four to ten exceptions. Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you've got 80 exceptions in your throws clause. It just balloons out of control.

Anders Hejlsberg:可伸缩性问题与版本性问题有关。在小的,受控异常是非常诱人的。通过一个小示例,您可以显示您确实检查了是否捕获了FileNotFoundException,这不是很好吗?当你只调用一个API时,这没问题。当你开始构建大型系统时,麻烦就来了,在这个系统中你要和四五个不同的子系统交流。每个子系统抛出4到10个异常。现在,每次你沿着聚集的阶梯向上走,你就会有一个指数等级在你下面你必须处理的异常。最后必须声明可能抛出的40个异常。一旦你将它与另一个子系统聚合起来在你的抛出子句中有80个异常。它失去了控制。

In the large, checked exceptions become such an irritation that people completely circumvent the feature. They either say, "throws Exception," everywhere; or—and I can't tell you how many times I've seen this—they say, "try, da da da da da, catch curly curly." They think, "Oh I'll come back and deal with these empty catch clauses later," and then of course they never do. In those situations, checked exceptions have actually degraded the quality of the system in the large.

在大型的受控异常中,人们会完全避开这个特性。他们要么说,“抛出例外”,到处都是;或者,我不能告诉你我见过多少次这个——他们说,“试试,大,大,大,大,大,抓住大卷。”他们会想,“哦,我会回来处理这些空的捕捞条款的,”然后他们当然不会这么做。在这些情况下,受控异常实际上降低了系统的质量。

#5


5  

OK, I wasn't going to reply, but this is taking too long to get closed and got many answers on one side of the fence, so I feel the need to weigh in on the other side.

好吧,我本来不想回答的,但是这需要很长时间才能结束,在篱笆的一边有很多答案,所以我觉得有必要在另一边权衡一下。

I support checked exceptions -- when properly used -- and believe that they are a Good Thing. I've heard all of the arguments above many times, and there is some merit in some of the arguments against checked exceptions. But on net, I think they are positive. Having programmed in C# and Java, both, I find C# programs are more difficult to make stable against Exceptions. The great thing about checked exceptions is that the JavaDoc is guaranteed to tell you that the Exceptions can be thrown from that method. With C#, you rely on the coder to remember to tell you what exceptions may be thrown from any given method, and also what exceptions may be thrown from any method called by that method, and so on.

我支持检查异常(如果使用得当),并相信它们是一件好事。我已经听过以上所有的论点很多次了,有些反对检查异常的论点是有价值的。但在网上,我认为他们是积极的。在编写了c#和Java之后,我发现c#程序更难在异常情况下保持稳定。检查异常的好处是,JavaDoc保证告诉您可以从该方法中抛出异常。使用c#,您需要依赖编码器记住可以从任何给定的方法中抛出什么异常,以及可以从该方法调用的任何方法中抛出什么异常,等等。

If you want to create 5-9's reliable code, you need to know what exceptions can be thrown from code that you call, so you can reason about what can be recovered from and what must cause you to abandon what you are doing. If C#, you can do this, but it involves a lot of trial and error until you have seen all of the possible exceptions that can be thrown. Or you just catch Exception and do your best.

如果您想要创建5-9的可靠代码,您需要知道可以从调用的代码中抛出哪些异常,这样您就可以推断可以从哪些代码中恢复,以及哪些代码必须导致您放弃正在做的工作。如果c#,您可以这样做,但是它需要大量的尝试和错误,直到您看到所有可能抛出的异常。或者你只是抓住例外,尽你最大的努力。

There are pros and cons to both approaches, that of Java and C#. Reasoned arguments can be made in favor of both, and against both. Again, on net, I prefer the approach chosen by Java, but were I to re-write Java today, I would change the APIs to change some checked exceptions into runtime exceptions. The Java API is not consistent in its use of checked exceptions. And as someone else said, it took far too long for Exception chaining to appear as a standard API feature and a part of the JVM.

Java和c#这两种方法都有优缺点。理性的论点可以是对两者都有利的,也可以是对两者都不利的。同样,在net上,我更喜欢Java选择的方法,但是如果我今天重写Java,我将更改api,将一些已检查的异常更改为运行时异常。Java API在使用已检查的异常时不一致。正如其他人所说,异常链接作为标准的API特性和JVM的一部分出现的时间太长了。

However, the charges that are lain at the feet of checked exceptions too often fall into the category of, "lazy programmers misuse this language feature." That's true. But that's true of many languages and their features. The "lazy programmer" argument is a weak one.

然而,在检查异常的脚边的指控常常属于“懒惰的程序员误用了这个语言特性”的范畴。这是真的。但是,许多语言和它们的特性都是如此。“懒惰的程序员”的论点很弱。

Let's address the main complaints that don't fall into the "lazy programmer" bucket:

让我们来谈谈不属于“懒惰程序员”范畴的主要抱怨:

  1. Versionability - yes, throwing a new Exception in a new version of your code will break compilation for clients who blindly drop in your new JAR file. IMO, this is a good thing (as long as you have a good reason for throwing an additional checked exception), because the clients of your library have to reason about what they need to do with this behavior change. If everything is unchecked, then your clients don't necessarily have any clue (until an Exception occurs) that your behavior has changed. If you are changing the behavior of your code, then it's reasonable for your clients to have to know about this. Have you ever updated to a new version of a 3rd party library only to find its behavior has invisibly changed and now your program is broken? If you make a breaking behavior change in your library, you should break automatic compatibility with clients using earlier versions of your library.

    版本性——是的,在代码的新版本中抛出一个新异常将破坏对盲目地插入新JAR文件的客户机的编译。在我看来,这是一件好事(只要您有一个抛出额外检查异常的好理由),因为您的库的客户必须考虑如何处理这种行为更改。如果一切都未检查,那么您的客户端并不一定知道(除非发生异常)您的行为发生了变化。如果您正在更改代码的行为,那么您的客户必须了解这一点是合理的。您是否曾经更新到一个新版本的第三方库,却发现它的行为已经无形地改变,现在您的程序被破坏了?如果您在库中进行了中断行为更改,那么应该使用库的早期版本来中断与客户端的自动兼容性。

  2. Scalability - If you handle checked exceptions properly by translating them to a specific checked (or unchecked) exception appropriate to your layer of the API, this becomes a non-issue. That is, if you code properly, this problem disappears. And doing so, you properly hide your implementation details, which your callers shouldn't care about anyway.

    可伸缩性——如果您通过将其转换为适合您的API层的特定检查(或未检查)异常来处理检查异常,这将成为一个无关紧要的问题。也就是说,如果你编码正确,这个问题就消失了。这样做,您就适当地隐藏了实现细节,而您的调用者无论如何都不应该关心这些细节。

Too often, this is simply a religious issue with people, and that's why I get (unnecessarily, I know) irritated. If you have a religious aversion to checked exceptions, that's OK. If you have a reasoned argument against checked exceptions, that's OK. I've seen good reasoned arguments (that I mostly don't agree with, but still...). But mostly I see bad arguments against checked exceptions, including arguments which were fair and reasonable when talking about Java 1.0, but which are no longer applicable given modern releases of Java.

很多时候,这只是人们的宗教问题,这就是我为什么(我知道,这是不必要的)生气的原因。如果你对受控异常有宗教厌恶,那没关系。如果您对已检查的异常有一个合理的论点,那就可以了。我看到过很有道理的论点(我大多不同意,但仍然…)但大多数情况下,我看到的都是针对已检查异常的糟糕参数,包括在讨论Java 1.0时公平合理的参数,但在现代Java版本中,这些参数不再适用。

#6


2  

In practice it is better to use checked exception handling as it allows for more detailed information when your app begins flooding error logs at 2AM and you get a call to do some debugging...

在实践中,最好使用检查异常处理,因为当您的应用程序在凌晨2点开始处理错误日志时,您会得到一个调用来进行一些调试…

#7


2  

In my opinion there exist cases where checked exceptions are appropriate. There are probably features that could be done differently in Java to better support them. It isn't without difficulties (for instance, in some situations you might want an exception checked, in others not). Java does, of course, support unchecked exception types as well.

在我看来,有些情况下检查异常是合适的。在Java中可能有一些特性可以用不同的方法来更好地支持它们。这并非没有困难(例如,在某些情况下,您可能希望检查异常,而在其他情况下则不是)。当然,Java也支持未检查的异常类型。

The sort of exceptions that are suitable to be checked should generally be documented. The best place to document is in the code. The populist approach is just to botch it and only consider the happy case.

通常应该记录适合被检查的异常。编写文档的最佳位置是在代码中。民粹主义的做法只是把事情搞糟,只考虑令人满意的情况。

#8


0  

Checked exceptions are great as long as they are recoverable or not due to programming errors like an invalid index acces to a ResultSet. Otherwise they tend to pollute code layers and APIs by forcing the coder to declare things like IOException in many method signatures and giving nothing really useful to the client code.

检查过的异常是很好的,只要它们是可恢复的,或者不是由于编程错误(如无效的索引进入ResultSet)。否则,它们会迫使程序员在许多方法签名中声明IOException之类的东西,并且不会给客户端代码带来真正有用的东西,从而破坏代码层和api。

#9


0  

I think in most cases checked exception are a waste of time. They entrap to things like the antipattern mentioned by randolpho or extensive creation of custom exceptions to decouple your implementation from used libraries. Beeing rid of this 'feature' lets you focus on what you want to do.

我认为在大多数情况下检查异常是浪费时间。它们会捕捉到randolpho提到的反模式或者大量创建自定义异常来将实现从使用的库中分离出来。删除这个“特性”可以让你专注于你想做的事情。

#10


0  

The only thing I'dd like the compiler to check for me is whether a function throws exceptions or not. What specific exceptions that can be thrown doesn't matter. The thing is that in my experience, there are a lot functions that don't throw anything, and it would be nice if this was documented in the function specification. For these functions you don't have to worry about exception handling.

我唯一想要检查的是一个函数是否抛出异常。可以抛出哪些特定的异常并不重要。在我的经验中,有很多函数不会抛出任何东西,如果在函数规范中记录这一点就更好了。对于这些函数,您不必担心异常处理。