Overview of Exceptions
There are quite of a few things that are just laws of Object-Oriented development, and one of those is that exceptions should be avoided. If you can prevent an exception from being thrown, do it. In the world of managed runtimes, particularly Java’s JRE and .Net’s CLR, objects are “thrown” to communicate errors. In a try/catch block, the language limits objects that can be thrown to ones that derives from System.Exception, or java.lang.Throwable in Java. When an object is “thrown”, the runtime stops and assembles the callstack and some other information and gives code at all levels of the callstack an opportunity to catch the thrown object (exception) and do something with it. If the exception is never caught, the runtime with catch it and terminate the program.
Clearly, exceptions being thrown in code is a bad thing, and it signals and unstable state in the program. It may be a huge bug, or the network may have gone down. Either way, and exception is thrown. Proper error handling with catch the exception at a point high enough in the callstack where the program can actually make a decision to do something about it.
Swallowing exceptions (wrapping code in a try/catch where the catch block is empty) leads to less feedback. An exception will happen, but it will be swallowed, and you won’t know about it. As soon as you start swallowing exceptions, they will start happening without being noticed. Debuggers pay special attention to exceptions, so swallowed exceptions (thrown, immediately caught, and ignored) will slow down the debugger with each occurrance.
Go to the Debug menu in Visual Studio and select Exceptions. CTRL+ALT+E is the shortcut. Check the checkbox for “Common Language Runtime Exceptions”. Now when you start your debugger, it will break when a managed exception is thrown. It will break on the line from which the exception originates. You can use this technique to find all the exceptions that are happing in your software right under your nose. If you refactor to keep those exceptions from happening, you’ll see a marked improvement on debugger load time (provided there were a large number of exceptions happening previously).
The alternative to debugging often is automated tests. When each small piece of code is verified independently, you don’t have much occassion for running the full application in debug mode. If you have unit tests as part of your automated test suite, a failure will point to the exact place where you have the problem.
Rule to live by
Fail fast. Fail fast. If your software is going to fail, make it fail quickly so that you can get the feedback earlier and fix it earlier. Don’t hide the problem by ignoring it or burying it in a log file that’s already verbose. If you are unfamiliar with this concept, read this article by James Shore.
Don’t use an exception as a return value. In an ideal situation, your application should run with 0 exceptions. You may have a library that swallows an exception, and that would be unfortunate, but keep your application code clean. If you can anticipate an exception happening, perform some checks to avoid it being thrown.
How to keep an eye on exceptions
Use Perfmon. Watch the counter “.Net CLR Exceptions\# of Exceps Thrown”. The number should be zero in an ideal situation. If you have an app that can’t avoid some exceptions, you can watch “# of Exceps Thrown / sec”. This number should be close to zero. If your application is constantly throwing exceptions under ideal circumstances, you have some work to do.
[tags: exceptions, programming, c#, java, failfast, objectoriented, development, .net, clr]