可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm looking for a definition of 'exceptional' in terms of unit testing or OOP principles. Several times on SO when talking about exception throwing, I've seen comments like, "Well, I wouldn't consider getting Foo from Bar to be exceptional." (Is there a trollface emoticon?)
I did some googling and an answer wasn't immediately forthcoming. Are there any good definitions, rules of thumb, or guidelines that are more objective than opinion or preference? If one person says "this is exceptional" and another says "no, it isn't", how does one resolve this dispute?
Edit: Wow, so there are a number of answers already, with comments that disagree, and they all seem to be the opinion of the answerers :P Nobody's referenced a wikipedia page or an article a snippet from a greybeard. The 'meta-answer' I'm taking from this is there is no agreed-upon rule of thumb for when to use exceptions. I feel more confident employing my own, personal, idiosyncratic rules when choosing to throw exceptions.
回答1:
I disagree with the other answers about something needing to be "exceptional" to warrant an "exception". I say "exceptional" is any time you want to break the normal flow of control in the way you can by throwing an upward-propagating object.
Exceptions are a language feature. They're a means to an end. They aren't magical. Like any other feature, when you use them should be guided by when you need what they can provide, not by what they are called.
回答2:
It's exceptional if:
It is a failure condition. AND
It happens infrequently and
unexpectedly. AND
There is no better mechanism for
reporting it.
edit
Stealing blatantly from Dan Weinreb's blog entry, which Ken posted about here, I'd like to offer the following summary of what exceptions are about.
The method's contract defines how
(and whether) unusual conditions
(ie. failures) are signaled. If the
method says something is an
exception, it just is. Of course,
this leaves open the question of how
we should design the contract.
Exceptions have the benefit of not
requiring any checking by the
caller, as well as naturally
bubbling up until they are caught by
something able to handle them. They
can also contain significant detail,
user-readable text and stack traces.
These features make them ideal for
failure cases that prevent further
processing but are not predictable
or common, or where explicit
error-handling would be disruptive
to code flow. They are especially
good for errors that "should never
happen" but are catastrophic in
effect (such as a stack overflow).
Flags, error codes, magic values
(NULL, nil, INVALID_HANDLE, etc.)
and other return-based mechanisms do
not commandeer flow, and are
therefore better suited for cases
that are common and best handled
locally, especially those where the
failure can be worked around. As
they operate by convention and not
fiat, you cannot count on them to be
detected and handled, except that an
invalid value may be designed to
cause an exception if actually used
(such as an INVALID_HANDLE being
used to read).
When using exceptions in robust
code, each method should catch
unexpected exceptions and wrap them
inside an exception from the
contract. In other words, if your
method does not promise to throw
NullReferenceException, you need to
catch it and rethrow it inside
something more general or specific.
They're called exceptions, not
surprises!
回答3:
The general rule of thumb is:
- Use exceptions for errors that you anticipate, and that could happen.
- Use assertions to deal with errors that could never happen.
回答4:
IMHO, an exception should be thrown, if the further execution of program would result in fatal error or unpredictable behaviour.
回答5:
Personally, I think that this kind of discussion is a pure waste of time and that "what is exceptional" is the wrong question to ask.
Exceptions are just another flow control mechanism with certain advantages (flow of control can pass multiple levels up the call stack) and disadvantages (somewhat verbose, behaviour is less localized).
Whether it's the right choice should not be determined by its name. Would we have these discussions if exceptions were simply called "bubbleups" instead?
回答6:
When someone rips out the power chord, that is exceptional, most of the other situations are expected.
回答7:
Well, Exceptional programming case is one which deviates the program flow from normal stream and it might be due to:
::A h/w of s/w fault that programmer cannot handle in given situation. Programmer may be not sure what to do in those case and he left it to the user or tool/library which invokes this code.
::Even programmer might not be sure the exact environment in which his code will be used and hence it is better to leave the error handling to the one who uses the code.
So exceptional case with a program might be is to use it uncommon environment or with uncommon interactions.
Again uncommon interactions and unknown environments refers to the designers point of view.
So deviation from Normal is exceptional and again it is based on the point of view and context of the programmer.
Is it too round and round?:D
回答8:
Exceptions are useful when something has gone wrong that's outside the immediate scope of the problem. They should almost never be caught close to where thrown, since if they can be satisfactorily handled there they can be satisfactorily handled without throwing anything.
One example is C++'s containers, which can throw bad_alloc
if they can't get the memory they need. The people who wrote the container have no idea what should happen if the container can't get memory. Perhaps this is an expected thing, and the calling code has alternatives. Perhaps this is recoverable. Perhaps this is fatal, but how should it be logged?
Yes, it's possible to pass back error codes, but will they be used? I see lots of C memory allocations without tests for NULL, and printfs that just discard the return value. Moreover, lots of functions don't have a distinguishable error code, like negative for printf and NULL for memory allocation. Where any return value can be valid, it's necessary to find a way to return an error indication, and that leads to more complication than most programmers are willing to deal with. An exception cannot be ignored, and doesn't require lots of defensive code.
回答9:
The best discussion of this that I've seen is in Dan Weinreb's blog: What Conditions (Exceptions) are Really About.
It's ostensibly about Common Lisp, whose condition system is like a more flexible form of exceptions, but there's almost no Lisp code and you don't need to be a Common Lisp programmer to follow the concepts.
回答10:
And another good rule of thumb - never use exceptions to catch conditions that you can catch in code.
For example if you have a method divides 2 variables, don't use an exception to catch divide by zero errors, instead do the necessary checks beforehand.
Bad code example:
float x;
try {
x = a / b;
{
catch (System.DivideByZeoException) {
x = 0;
}
Good code example:
if (b == 0)
return 0;
Exceptions are generally expensive.
回答11:
I'm not going to get into the holy war of whether exceptions ought to be used as flow control or not, but rather I'm going to focus on what an exceptional event is...
Oracle, in the context of Java, defines an exceptional event this way:
An exception is an event, which occurs
during the execution of a program,
that disrupts the normal flow of the
program's instructions.
Of course, this definition applies to exceptions in general, not just in Java.
The key here is "disrupts the normal flow". In other words, the program fails in such a way that it can't complete its instructions.
A program has a defined range of functionality. When all your program's functionality is accounted for (including handling of invalid input), anything that is left over is an exception and likely a bug.
I believe there are two kinds of exceptions:
- Bugs, which are introduced into the program by the programmer.
- Uncontrollable situations, like someone else said, pulling the power plug. Of course, the program wouldn't get to throw an exception in this case, but if it could, it would. Other exceptions might be some sort of failure within the OS or network that the program relies on.
For instance: A program ought to handle invalid input from users as part of its normal flow, because users will of course give you invalid input at some point. This isn't a disruption because the program's functionality should be able to parse input and see if it's valid.
However, if a programmer somehow allows null to be passed to a method that isn't supposed to accept null, then that's an exception. The method's behavior is undefined for values of null. While it's a gray area, the reason I don't necessarily consider this invalid input like the above example is because the method has a certain spec, and by coding something into the program that passes a value violating that spec, it's a bug.
回答12:
Exception should be used for things you can't control within your program. Most of time, this means you are working with external files or an internet/database connection.
For user input, YOU should control it before you compute anything with it.
Of course there is a limit to what you can expect and you should stop before reaching the "armless person exception". We came up with this term when we were programming something with movement detector and one of our friend was over-protecting his code. So we told him: "Yeah so, did you control what happens if an armless person put his hand in front of the detector?". We got a good laugh with it :)
回答13:
Two main cases:
- When calling code has asked you to do something unreasonable.
- When outside dependencies have left you with nothing reasonable to do.
In the first case consider a method int IntegerDivide(int dividend, int divisor)
. Now, we all know we should catch conditions like dividing by zero ourselves. However, if the calling code has asked this method to divide by zero, it's too late - it's not our code that is deciding to divide by zero, it's the calling code. The only reasonable thing to do here is to throw an exception.
In the second case, consider a component that reads a file and returns some sort of computed result, and what it should do when it fails to open the file. If we had access to the user interface, then we should catch this failure and present a useful and informative message to the user. However we don't, as we're writing a component for use by other code closer to the UI. Again, the only thing we can do is throw an exception (and hope that the calling code does the right thing when it comes to informing the user).