Scott Hanselman

Good Exception Management Rules of Thumb

August 31, 2006 Comment on this post [13] Posted in ASP.NET | Learning .NET
Sponsored By

I was coming up with Good Exception Management Rules of Thumb for .NET. Here's what my brainstorming came up with. What do you have as good Rules o' Thumb?

  • Exceptions are exceptional and should be treated as such. If something exceptional, unusual, or generally "not supposed to ordinarily happen" then an exception is a reasonable thing to do.
    • You shouldn't throw exceptions for things that happen all the time. Then they'd be "ordinaries".
  • If your functions are named well, using verbs (actions) and nouns (stuff to take action on) then throw an exception if your method can't do what it says it can.
    • For example, SaveBook(). If it can't save the book - it can't do what it promised - then throw an exception. That might be for a number of reasons.
  • If you can, throw an exception that means something, and if there's an exception that already exists that matches what happened semantically, throw that.
    • Don't create a HanselmanException just because you're writing the Hanselman module unless you're adding data or valuable semantics to the type.
      If you are building a framework (or even if you're not) throw ArgumentExceptions and ArgumentNullExceptions liberally. Just as your method should throw if it can't do what it promised, it should throw if you supplied it with crap input.
  • If something horrible happens (something exceptional) then you need to decide if you can keep going.
    • Don't catch exceptions you can't do anything about. It's likely if you could do something about it, it wouldn't be exceptional, and you might consider calling TryParse, or File.Exists, or whatever it takes to prevent that exception.
  • There are reasons to swallow exceptions (catch (Exception ex)) but they are few and far between and they should be logged if appropriate and documented liberally.
    • Remember always if you do catch an exception and intend to rethrow it, then use throw; not throw ex; lest you lose your call stack and good bits of context.
  • Create a global error handler that logs everything.
    • A user shouldn't ever see an exception dialog or ASP.NET Yellow Screen of Death, but if they do, let them know that you've been notified.
    • {smartassembly} is an easy way to make this happen. So is ELMAH for ASP.NET. (I freakin' love ELMAH)
  • Yes Response.Redirect in ASP.NET causes an internal exception. Yes, it's a bummer, but there's a reason. It was an easy way to stop execution. If you don't like it, call its overload and stop page execution yourself. Personally, I don't sweat that one, but then I avoid Redirects, too.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
August 31, 2006 3:38
I'd probably mention that there is a difference between how you handle exceptions when writing a library vs writing an application. For example, a library should generally just propagate exceptions unless it's absolutely sure it can handle it in the best way possible.

Applications can often catch exceptions and provide the user a means to correct an action. But in either case, if the code can't handle the exception, don't even catch it.

Retry logic in a library when an exception occurs, for example, can be a bad idea. Let the consumer choose what to do with the exception.

If you do create your own exceptions, don't derive ApplicationException. Derive from Exception. In a library, if you need to derive from a more specific Exception such as ArgumentException, consider the fact that existing clients that are catching ArgumentException will catch your new exception, but maybe shouldn't be.
August 31, 2006 4:20
Great list Scott. How about:

* Catch exceptions at boundaries, for example WCF (wrap exceptions in FaultException), WSE (wrap exceptions in SoapException)
* Make custom exceptions serializable
August 31, 2006 4:53
I write a number of Windows services and I always ensure that my main processing thread and any functions that are invoked in a managed thread pool always have a generic catch(Exception ex) in with proper logging of the exception. An unhandled exception at those levels could easily cause the service to shutdown unexpectedly. The program (especially if it has no front end) should always catch any error that may cause it to shutdown and recover from it (ie, if it is a server program, just disconnect the user or something to ensure it can continue running).
August 31, 2006 5:49

Here's another interesting blog post on this topic.
August 31, 2006 8:22
DavidB - Excellent! I like "catch exceptions at boundaries" - catchy and common sense. Good stuff.
August 31, 2006 8:44
For libraries mostly: Create a static, internal ExceptionHelper class and put all the logic for the default exceptions (ArgumentNullException, ArgumentOutOfRangeException, InvalidOperationException, etc.) in that class.

It makes methods more readable, especially if there are three arguments to a method, all with restrictions. And you save lots of typing.

The common ones I use:

ThrowIfObjectNull(object o)
ThrowIfStringIsNullOrEmpty(string s)
ThrowInvalidOperationException(bool condition)

You can get pretty fancy with these. For example, Brad Abrams and Krzysztof Cwalina recommend putting all of your custom exception messages in resx files. So I do that, 'cause I'm anal and we may have to internationalize some time.
August 31, 2006 9:23
if you have to throw an exception from inside an exception handler, always remember to include the existing exception as an inner exception.
catch (ArgumentException ex))

if (can handle it) {...
//handle it
} else {
throw new exception("a genuinely useful description taking into consideration the fact that sometimes we *can* handle this kind of exception, describing the known facts before making suggestions/jumping to conclusion", ex);
//note that ex is passed to the inner exception parameter, so that the stack trace etc isn't /lost/ lost.
August 31, 2006 13:50
Exceptional list of pointers...

...(urg) sorry!, couldn't resist
August 31, 2006 17:06
Regarding the .TryParse/File.Exists rules, I like to shorten it to:

"Don't pee your pants to see if your fly is unzipped"
August 31, 2006 18:38
"You shouldn't throw exceptions for things that happen all the time. Then they'd be 'ordinaries'.
"...throw an exception if your method can't do what it says it can."

If you have "ordinary" failure cases that crop up so often that exception handling is harming your performance enough to be a problem, you should add a function with a different name that can fail without throwing an exception (and whilst still completing its "verb").

E.g. The case where a string containing non-numeric data was passed to Double.Parse() turned out to be common enough that they added Double.TryParse().

That tip came from Jason Clark at DevWeek 2006. He gave a great talk about exceptions. His rules were about the same as yours.

He related an interesting titbit from the CLR team. Apparently they are somewhat sorry that they implemented CLR exceptions using SEH (which is what makes them slow). They told him there is no reason they have to be done that way, and they will seriously think about reimplementing them fi they ever become "one of our customers' top 5 real-world performance issues". :-)
September 23, 2006 1:35
I'm curious about your last point. Why do you avoid redirects?
September 23, 2006 3:21
I'm curious about your last point as well. I think I understand why, but not so much how. It seems impossible to avoid Redirects completely. Could you talk about this more?
September 23, 2006 3:41
I'm saying that I like to avoid this:

* Load, click, postback, immediate redirect to next page

I like:

* Load, click, postback, show next page (either via Transfer or via a Panel/Workflow

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.