Over the years I’ve seen both ends of the spectrum when it comes to handling unexpected errors in code. Having over zealous exception handling in an application is just as bad (or worse – as it hides the problem!) than having none. Junior developers tend to go to the extreme, capturing and suppressing everything as they’ve had it drummed into them that code must be “stable” and never “fail”. Well, I guess it depends on what definition you put on stable and failure. Personally, I would rather have an application that behaved, did what it was told and didn’t trash my data when something unexpected happens – I’ve seen lots of commercial apps, especially Financial related, that trash your data when they crash – not exactly fun when you then spend the next three weeks working out what’s missing since the last backup.
There is an excellent article that came out in 2005 on Exception Handling Best Practices in .NET (http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx), however, I have to admit, I don’t always follow them perfectly.
Personally, I try and follow these rules:
Never swallow an exception
If an exception has occurred, its because something isn't right. At the very least, log the FULL details (so that includes the stack trace) centrally. You do have centralised logging, right?
This is actually probably one of the most contentious issues among developers – some say you should never grab exceptions and only log them, instead you should let it bubble up to a higher layer to be handled. My feelings are that if the exception can be safely handled, without causing risk to user data, then its safe to handle it – as long as its logged!
Never re-throw an exception
You’ve caught an exception. You want to pass it further back up the stack. So don't use new Exception();, or throw ex; as this will hide the original exceptions call stack and message / innerexception information. Simply throw it again (i.e. throw; ).
If an object implements IDisposible, use using
There is always a reason why an object implements IDisposible, and if it does, be sure to use the using keyword to ensure all resources are released when the objects done.
If you are expecting an exception, only catch that specific exception
Ok, I know that sound strange, but bear with me! If you are handling conversion from GUI controls to, say, decimal values, then the odds on you are going to hit exceptions – and usually conversion releated ones. Then instead of trapping Exception, you should be trapping specific exceptions such as ArgumentNullException, FormatException or OverflowException.
If all else has failed, record the detail before you exit
Too many applications these days just throw up the generic “something knackered” .NET exception dialog and then exit. What do you do next? Start it up again. And what if it dies again? How are you supposed to report the problem? If you are building an application, make use of the Application.ThreadException and AppDomain.UnhandledException (note that latter is really AppDomain.CurrentDomain.UnhandledException, and you will need to hook this on each AppDomain, obviously) – record as much information as you can about the exception, and THEN terminate.
You can even go one step further and have the application automatically report the fault – I tend to do this for any programs that are being used internally in an organisation, and I know that they will always have access to reporting APIs (or SQL Servers).
When you compare .NET to my early days developing in Delphi, there is a stark contrast to the behaviour you adopt with Exceptions. In Delphi, you used Exceptions a lot to control your program flow (you threw exceptions pretty much when anything went even a little wrong). These days, things are little more restrained – exceptions are heading to the realm of reserved for cataclysmic events that will cause the application to no longer be reliable.
Ultimately, no matter what happens when an exception is trigger, it’s down to the developer to take a reasoned approach to what to do next. Is it possible to continue? Or more importantly, is it possible to continue with no risk to stability, user data or other systems? If in doubt, exit!
I’ve personally built a series of frameworks that allow me to handle exceptions, logging and reporting very effectively, however, many developers are not in a position to do that (especially if you are working in a very small team, or are perhaps an independent). Or maybe you’d just rather use a product to help you. If that's the case, I can heartily recommend checking out Exceptioneer.