Posted on 5/14/2009 10:31:23 AM by Justin Etheredge
I received a question in an e-mail today and it was asking about custom exceptions. There was a few things that the person was curious about, and it mainly revolved around a few topics:
- How do I design exceptions in my applications?
- What are my best practices for exception handling?
- When should objects throw exceptions?
- Do I put exception classes in separate assemblies?
I decided that I would write a blog post about my response to these questions, since my ideas about exceptions definitely differ from what I have read over and over in different recommendations on .NET development. I'm not asking that you follow my practices, I am letting you know what my practices are, so that you can think about them and hopefully provide feedback. The answers to these questions may vary depending on the requirements of the individual application, but on the average case, these would be my answers.
1) When it comes to designing exceptions in my application, I take an extremely pragmatic approach. I learn towards almost always using Framework exceptions before creating custom exceptions. Exceptions like ArgumentNullException, ArgumentOutOfRangeException, InvalidOperationException, etc... should all be used before creating custom exceptions for your own case. If I do create custom exceptions, I try to keep them extremely generic, since I believe that the important info in an exception is usually in the Message and not the type. The overhead of creating tons of custom exceptions is rarely worth it, when a human is usually the one that ends up looking at an exception. Spend your time creating good meaningful error messages in exceptions, rather than focusing on creating tons of exception types.
2) I don't care for the term "best practices", but I understand what they mean. In my case, they aren't "best practices" though, just merely suggestions. :-) I usually take the approach of letting exceptions go. I never catch an exception unless I have something specific to do with it. I've seen many applications where they catch exceptions all over the place and log the exception and then rethrow it. No no no. In most cases there should be some sort of global error handling/logging. Maybe not global, but at least at a much higher level than at the class level.
When it comes to catching exceptions, I think that there are usually only three reasons to catch an exception:
- If you need to add info to the exception. This is generally done by wrapping the exception.
- You need to perform some cleanup or logic after an exception is thrown. Even then, the exception is usually rethrown.
- You need to recover from the exception, and you are absolutely sure that you know how to do this.
I find that 99% of all cases are number 1 and number 2. Generally speaking an exception is because something bad and unexpected happened, and so you should let it rise up, notify the user, do some logging, and then move on. In general, it should terminate the application (or request). Once an exception is thrown, there are very few cases where you can be sure that you are able to recover. I've seen many pieces of code where people put exception handlers in to try and recover from exceptions only to have them cause errors further down the line due to side effects of the exception that the developer could not have anticipated. This can make thing exponentially harder to debug.
3) Only throw exceptions in exceptional cases. That is why they are called exceptions. Reserve exceptions for things that keep the application from performing its duties properly. The one place I see this violated a lot is with validation. People will validate some piece of data coming from the user, and when it is invalid they will throw an exception and then usually catch it only a few lines later in order to do something with it. Most people will cite this as bad practice because throwing exceptions is expensive. If you follow my blog then you'll know that I hate micro-optimizations in most cases, so to me the real travesty is in disrupting the control flow of your application for very little reason. Throwing an exception is like a goto with no target. Structured exception handling is great, but don't abuse it in order to circumvent the normal flow of your application.
4) I don't. I usually keep them close to the code using them. I might do this if I had a really large project and I needed to share my exceptions across several assemblies.
And one last thing, when you do rethrow an exception after some cleanup, make sure that you throw it like this:
try
{
//do some work
}
catch (MyException exc)
{
//cleanup
throw;
}
And not like this:
try
{
//do some work
}
catch (MyException exc)
{
//cleanup
throw exc;
}
The reason for this is simple, one maintains the original exception information (including the proper stack trace), the other does not. Calling "throw exc" now makes it look as if the "throw exc" line was where the exception was raised, making it potentially much harder to debug. Oh, and you save yourself some typing.
So how do you handle your exceptions?