September 22, 2007

Error Handling – Best Practices

I had a question from a friend about exception handling best practices earlier this week. He’d mentioned that it’s pretty hard to find good information about the topic. I rattled back a list of fairly generic dos and don’ts that we’ve all seen with exception handling before. As I thought about it a little bit more, I came to realize that we accepted a lot of these on blind faith, so I thought I’d take a little bit closer list of some of the error handling recommendations made in the .Net Framkework Design Guidelines.

Performance

Exceptions are fairly costly operations and will generally be considerably slower than a programmatic solution. How much slower? To find out, I wrote a quick-and-dirty test application. This application divides two values, but, instead of returning an error to the client, it returns a null when attempting to divide by zero. The project contains two implementations of the calculator object: the calculator using exceptions, and the calculator not using exceptions. The only difference between the two is the way the division by zero is handled.

In the exception version, we are defaulting the return value to null, and simply hiding the exception when it occurs.

While I was expecting there to be a little bit of a difference between the results, I was amazed at how big the difference was. On my machine, the object which was using exceptions ran 5000 iterations in 35 seconds. The version which explicitly checks the denominator value instead of using exception conditions ran in two milliseconds. [Note: Jon Skeet notes in the comments that this calculation is skewed, because I was running the code in the IDE at the time. He is correct. When running in the IDE, I get the 35 seconds to 2 milliseconds results. When running the compiled versions (Release or Debug), I’m seeing something on the order of 500 milliseconds to 2 milliseconds. It’s still pretty bad, but nothing on the order presented above.]

In general, this guideline is quite sound. If you are catching the general base class exception, you have no idea what has gone wrong, and shouldn’t attempt to recover from it. If an exception is thrown while writing to a file, for example, you do not know if the exception was because of an illegal file operation or due to an out-of-memory exception. It does take more time to research the appropriate exceptions that can be thrown by an object, but it can prevent a lot of pain in the long run.

The one exception I’ll often make to this rule is at the boundary of an object. If an exception has been thrown and has bubbled all the way up to the top of the stack, something truly unexpected has occurred. I’ll catch the generic exception object, log the exception to a log file, and then kill the application.

Use an Empty Throw

Section 7.2.2 also talks about using just an empty throw command when rethrowing an exception. In other words, use:

catch (Exception ex)
{throw; }

Note that I’m not using throw ex. The primary reason for this is to maintain the stack trace. If you look at the Rethrowing Exceptions application, you’ll note that the only difference between the GoodErrorHandling and the BadErrorHandling methods is that good uses throw, and bad uses throw ex. Both methods call MyFlawedMethod which returns an InvalidOperationException. The resulting stack traces are displayed in a message box.

Examining the stack trace messages closely, you’ll notice something. The line number in the top frame of the stack points to different locations, even though the same base exception was thrown. The GoodErrorHandling frame points to the actual location of the error. When BadErrorHandling threw a new exception, however, the stack was reset and now points at the error handling in BadErrorHandling. This makes chasing down the originator of the actual error much less fun.

Code safe!
Mike

Like this:

LikeLoading...

Related

Hi Mike, good comments regarding error handling. I like the advice of not throwing a generic exception handler – like you mentioned it costs a lot more time up front, but when the app is deployed, I think the errors returned will be more valuable and end up saving time debugging. One question or comment I have, why does everyone cast an exception variable ex in the catch block, even if not using it? I noticed you do that in your example above and you did the same thing in your GoodErrorHandling method. I’m somewhat anal in that since the VS designer squawks at me if there are unused variables, I usually leave them out.

Yup. You’re absolutely right about the variable not being used. If I turn on code analysis when I compile, it complains. (I always have code analysis turned on when I’m writing production stuff, but I’m not sure how it would react if somebody were to try and run the code samples on a non-Team Systems Developer version of Studio.)

I think the main reason the variable is almost always caught is because you will almost always want to do something to do with it. In the example I use in the article, I’m immediately rethrowing the exception. Typically, you would expect code to look more like:

I suspect that most of the situations where an exception is being caught without the exception variable, one of two things is happening.

A.) The exception is immediately being rethrown to be handled by a method higher on the call stack. If this is the case, there probably isn’t a need to catch it at all, as this will just happen naturally.

B.) The exception is being used to handle code flow, rather than using standard .Net functionality. This would be similar to my “bad” calculator example.

If you have parameterless exceptions that fall outside one of those criteria, I would be curious to see what you have.

If it’s taking you 35 seconds to throw 5000 exceptions, you’re probably running in the debugger. My laptop will throw about 21000 exceptions per *second*, last time I checked. Exceptions are much more painful in the debugger.

Running your tests, under the debugger 5000 iterations takes about 26 seconds (with exceptions), not under the debugger it takes 369ms. Yes, it’s still a lot slower than not using exceptions – but there’s a massive difference between debugger/non-debugger!

Hi Mike,
Thanks for the article. I have one small clarification. How about wrapping the exception in the examples mentioned and then logging the same. Do you think the performance will still be affected in that case and how about the stack trace issue getting resolved in the case of wrapping the exception being caught.

[…] in a Java itself, increasing burden on VM already strangled to death. As you already know, exceptions are costly, very costly, regardless of a programming language, platform or anything else, and best avoided, if […]