Saturday, June 27, 2009

Custom Exceptions: They'll Make Debugging Easier

One suggested practice that I read about over and over again in many software development books is the use of custom exceptions. The various books and blogs mention custom exceptions as a way to ease debugging and shorten development time. I recently made extensive use of custom exceptions in a console application that I wrote, and after a little trial and error, I came away impressed and will definitely vouch for the practice.

The first obvious advantage of using custom exceptions is the ability to add custom functionality to your derived class. Since you're designing the class yourself, you're able to totally customize the behavior of the class. You're also able to add detailed information about the application state that threw the exception. If you're logging exceptions (which you should be), then you can quickly check your logs to see detailed information regarding the exception and re-create the application state that threw it.

Another advantage of using custom exceptions is the more descriptive name given to your derived class. This much-touted advantage seems kind of petty when you first hear it. When I first read it, my thought was, “Ok, so?” However, it's a small feature that helps in a big way. When you're scanning over your exception log (the one you're logging your exceptions to, right?), it's actually a big help to look at the log and see a specifically-named exception. The extra descriptiveness helps more than you'd think.

The various books and blogs recommend using custom exceptions, even if all you do is derive from System.Exception and give the derived class a more descriptive name. My advice is to not go overboard with it. It is a trade off: You're adding a lot of extra code that doesn't really accomplish anything for the payoff of more descriptive exceptions. When simply deriving System.Exception and not adding any custom functionality, I recommend only doing so when you have a specific application state that you want captured.

Several caveats exist if you're writing custom exceptions. If your custom exception wraps another exception by catching the original exception and throwing the custom exception, make sure that you capture the original exception by setting your custom exception's InnerException property. The InnerException property can't be set directly, but you can set it by using the base.Exception(string message, Exception innerException) constructor overload.

The other caveat is the try/catch structure you've built. Keep in mind that in the above situation where you're catching a System.Exception and throwing your own, the stack trace of your custom exception will begin at the line where you threw the custom exception. If you aren't setting the InnerException as described above, you're going to lose the stack trace of the original exception that was thrown.

Also, remember if you're re-throwing an exception in a catch block, use the throw; statement without specificying the exception. This will preserve the stack trace, whereas throw [exception]; will begin the stack trace at the line where you're re-throwing the exception.