On Exceptions

A while ago, Justin Etheredge posted an article on best practices for when, when not, how and how not to program with exceptions in .NET.

For the most part, I agree with what he says in this article, but felt that I needed to blow my own horn on some of these issues since I get to see some fundamental misunderstandings (“misgrokkings”?) on the purpose of exceptions in code that is written by some of the guys here.

Part of me wants to start off with a tirade on the definition of the word exception, but I’ll spare you and skip that—suffice to say that exceptions really are that; they’re not supposed to be something we worry about or deal with during most of our programming—they’re … well … exceptional.  They’re supposed to be part of that whole managed thing that everyone keeps talking about.  Do you remember the good old days of C programming where everything returned an int to keep track of errors (even if that int being non-zero occurred only 1% of the time?).

In any case, a list of Smith’s rules around exceptions follow.

Rule 1: Relax, don’t Catch It

For the most part, really—carefully consider the reasons for catching exceptions.  The only times you should catch exceptions are:

  • When you need to do some kind of teardown or cleanup, regardless.  That’s why we have finally—so this isn’t really a reason for catching an exception after all;
  • when you need to align the exception with the context of what you were trying to do—a typical example of this is where an unexpected runtime exception such as NullReferenceException is thrown and you want to provide some more problem context, so wrap it in a more meaningful exception and throw;
  • When you absolutely have to recover gracefully from a condition that would normally terminate the application—be really careful here – continuing on in what will essentially be an errant state can lead to even more problems;
  • In order to log an error using a logging subsystem.  There should really only be one of these—just before the exception is about to fall through to the runtime.

Rule 2: Be Specific

When catching exceptions (and after some long, hard deliberation to reach this decision), try to be as specific as possible.  We’ll want to do:

catch (IOException ioException)
{
    // Stuff...
}

… and not:

catch
{
    // Stuff...
}

Rule 3: Don’t Throw Stuff Away

Ok, I couldn’t resist that small pun—by doing this sort of thing, we are throwing information away:

catch (NullReferenceException e)
{
    throw new InvalidOperationException(
        “The dooda can’t be used with a gidgimigadge: “ + e);
}

We would rather want to do:

catch (NullReferenceException e)
{
    throw new InvalidOperationException(
        “The dooda can’t be used with a gidgimigadge“, e);
}

The latter preserves all stack trace information, the former throws it away.

What you hide will bite you

Someone once said to me:

Ignore small problems, they’ll either go away or become big problems.

I’ve tried to keep this article positive, but I just have to have a word on this one … as someone pointed out, everybody loves code they can hate, and this is my personal sleeping blanket:

try
{
    // Stuff...
}
catch { }

This is known as exception burying.  image

In my earlier days as a developer, tinkering with various obscure scripting languages (the more arcane, the better), I bumped into Perl.  When they say that you can do something in more than one way with Perl, that’s a gross understatement—this is why I said my goodbyes to the language some time ago.  Apart from not being able to remember all of the syntax due to it’s sheer size, because the syntax is so ambiguous, you can end up writing stuff that is perfectly legal but doesn’t do what you want it to.  My initial self-high-five after writing my first Perl program and having it run without any errors quickly turned to dismay and frustration when it didn’t appear to work.  That’s because unless you “use strict” in Perl (or the ‘-w’ command-line switch), it just works … in particular, things like referencing variables that you’ve neither declared nor assigned to is just hunky-dory.

My point is: warnings and errors are good, not bad, they provide visibility into your program and give you hints about what may turn out to be problems later on.  They allow you to avoid days of trying to track a bug down because an errant condition is being buried.  Don’t bury exceptions.

A word on the appropriateness of exceptions being thrown

The .NET base class library provides a range of generic exceptions that can be used to report errors (including ArgumentException, InvalidOperationException, NotImplementedException, ArgumentOutOfRangeException and various others), and these should be used if at all possible.  The important thing though it to distinguish between exceptions that have been built to be thrown by your code, and ones that haven’t.

I can’t really think of valid situations where NullReferenceException and OutOfMemoryException should be thrown in your own code—these are things that you would catch (once again, that decision needs to be made carefully) but not throw.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current day month ye@r *