Not all exceptions are errors. Many developers have incorrectly been taught or assume that all exceptions are errors. In fact, a key clue is that they are called exceptions and not errors.
Dictionary.com defines an exception as something excepted; an instance or case not conforming to the general rule. Exceptions in code are used to modify standard program flow (i.e. the general rule) and communicate information to a higher calling level where they are trapped.
.NET, Delphi, C++ and Java all treat exceptions with the same rule and many code bases use exceptions for many tasks other than to signal errors. While signalling error conditions is the most common use of exceptions, it is not the only proper use. If errors were the only proper use, we would use the older term that Visual Basic and others used of error handling, instead of exception handling.
Exceptions can also be used to return status information. Imagine a large framework where the call stack is very deep, yet you need to exit out to the root and communicate some condition such as a cancel request from a user.
Should you modify each and every possible method that this can occur in? Doing so would require adding additional flags to each and every method signature possibly even as a reference argument, or creating a global flag in a static. Neither are good approaches. Just imagine how many
if statements this would require and the impact to the code in additional complexity and noise.
Imagine the following call stack:
Which is better? Modifying all three called methods to pass back a flag the user has cancelled? Or simply throw a typed exception in
DoSomethingDeeper and let
Button1Click catch it? Using an exception also allows generic resuse by any caller without imposing any abnormal prerequisites.
A cancel exception can be defined and easily thrown from anywhere. This exception is easily caught from the calling code, and has no impact on altering any other code to check for global flags or passed reference arguments. It also handles all program flow properly. If code needs to react to this condition, it can trap and then rethrow the exception. If you have already built in proper exception handling code for errors, usually the same code applies.
But is the user requesting a cancel operation and error? No. But it is an exception. There are many more examples of such behaviour. NUnit uses such exceptions, as does Indy Sockets (Connection closed gracefully), and Delphi's RTL (EAbort).
Errors are things that we do try to prevent, while exceptions may occur on purpose. Errors are things like "Not enough disk space", "Out of memory", "Cannot reach server".
Errors are things that can happen, but do not occur intentionally except during testing. Errors occur when something is wrong, but exceptions can occur also when things are correct but need "exceptional" attention. Think of an exception like an interrupt in hardware. Interrupts are used for error conditions as well, but they are also used to signal network activity, the reset button, and more.
Just because exceptions can be used for signalling something, their main purpose is altering normal program flow. They should not be used as general purpose signalling mechanisms. Exceptions themselves should be used as the exception, not the rule.
Throwing an exception is costly, relatively speaking. Exceptions are not fast mechanisms, nor need they be. You should not throw exceptions inside a loop, unless the exception also causes an exit of the loop. However used in a context such as a user cancelling processing, the overhead is one time and insignificant. In such cases, an exception will still be far faster than executing additional logic code you would have to write.
If the function is an endpoint function and an "exception" is likely, you can implement it like
TryParse. However this only works for endpoint functions, not in examples like the user cancel scenario.
- 11th October, 2006: Initial post