Catching Exception, SystemException, or ApplicationException; or coding empty catch blocks, aren’t simply "bad practices" — it’s just plain wrong. Here's why you shouldn't do it.
I am inspired to share my thoughts on this for a couple of reasons. One is in response and as a follow up to my friend Justin Etheredge’s recent post over at CodeThinked, where he briefly touches on this topic, but didn’t really dive too deeply into the wheres and whys. Second is to shamelessly plug my latest Wrox e-book just released today ;-), Robust ASP.NET Exception Handling (ISBN 978-0-470-50367-6), in which I examine this and other related topics in far more depth than I will in this post.
It is common to see developers trying to “overdo it” when it comes to catching exceptions. They reason (quite understandably) that any code could potentially throw an exception. Therefore, to be on the safe side, they attempt to wrap as much of their code as possible in try statements, then catch any exception that is thrown.
Of course, it is true that an exception can be thrown from nearly any code at any time. For example, even when calling very safe-looking code, the computer might suddenly run out of memory, which throws an OutOfMemoryException. Ask yourself — would you know how to handle an OutOfMemoryException in code? Me neither! Since there is absolutely nothing you can do about those kinds of errors, there is no point in catching them in the first place.
This should point out the futility of catching very general exceptions such as System.Exception or System.SystemException, or using a catch statement without an argument (sometimes called a general catch block). These constructs are shown below.
The third example is particularly insidious, as the MSIL generated by the C# compiler becomes catch(Object), which also catches COM and other non-CLR exceptions.
Though you may see this done in blogs and code samples from time to time (it’s very common on MSDN), these samples are usually for illustrative purposes only, and shouldn’t be used in production code, period. Remember, a general catch block catches any and every exception that occurs — even catastrophic system failures over which you have no control.
Blindly catching exceptions everywhere, or catching every possible exception, is a very bad practice, indeed. Remember, the MSDN documents all exceptions that can be directly thrown from methods and constructors in the .NET base class library. Use this as a guide in determining what exceptions might be thrown. Then, think about which specific exceptions you should catch, and why. Finally, develop a strategy to handle those specific exceptions, given the context in which it they are thrown.
There are legitimate reasons for handling exceptions. Among these are:
- Notifying the user, and if appropriate, allowing them to retry the operation
- Wrapping the exception in a new exception, appending additional relevant detail
- Wrapping a specific exception in a more general exception to prevent revealing sensitive details, such as exceptions thrown from a Web service
- Logging and/or notifying system administrators of the exception, and rethrowing it. (Even then, it’s generally not useful to routinely log handled exceptions unless you’ve got a good reason.)
- Using a finally block to clean up resources that might be abandoned if an exception occurs
If you don’t have a reason to catch an exception, or have no idea what you would do with it if you did, then don’t. Let it bubble up the call stack, and let your application-level exception handler take care of it. After all, that’s what it’s there for.
Only catch exceptions that you are prepared to handle.
The bottom line here is that well-written code should actually contain relatively few try-catch blocks.
I hope you’ll remember this the next time you’re tempted to catch(Exception). And don’t forget to tell your friends. ;-)