A Guide to Sexy Exceptions in .NET

System.NullReferenceException: Object reference not set to an instance of an object.

Whether you are a rookie developer or a weathered code monkey, you have seen this exception at some point in your work.  Chances are, if you are the latter of the two, you have dealt with tracking the issue down and dealing with it.  My goal, is to make that better.

The pain is that this exception does not tell you more than “hey guy, something was null” - if your lucky and running code with debugging symbols, you get a line number.  But it is up to you figure out what is null, why it is null, and how to fix it.

Beyond the immediate remedy to your bug, it should become immediately apparent that you have an exception design issue. 

An exception design issue is when an exception thrown to a user does not provide immediate actionable information without further investigation.

Here is What I Think

As hinted to above, my opinion of a good error is one that tells you:

  • What happened.
  • Why it happened.
  • How (if) it can be fixed.

Take a simple form where you fill out some info.  You wouldn’t (I hope) show the user a message like:

Form is not complete

We can do better than that:

The value you provided for Field X is invalid and must conform to this format.

Why should exceptions be treated any differently?

Consumers of your application could be users.  They could be developers.  They could be hackers from Mars.  Just because the users are more savvy, does not mean they can telepathically debug your application.

Your goal should be to provide the best experience to all of your users, especially when your application fails. 

How About an Example

One of the best examples I like to use is around using appSetting values from a configuration file.  Assume the following:

  • We are looking for an appSetting value in our config file called “ApplicationName”
  • We expect the value to be a string, and not empty/null
  • We do not have any error trapping if the value is not there.
  • The config file does not contain the key “ApplicationName”

So, given this, if we fire up our application we should get an exception.  Most likely, we get a NullReferenceException.

Stop For a Moment

Remember who your users could be and put yourself in their shoes.  Without the source code, do they have any idea what really is happening and what to do to fix it? - NO!

The most they can possibly know is that somewhere, someone forgot to put a check for null values in place.

Lets Make This Better

If our code looks like this…

string appName = ConfigurationManager.AppSettings[“ApplicationName”].ToString()

We know that the .ToString() call is what is throwing our exception.
For sake of the demonstration, we will leave this line as-is.

try
{
     string appName = ConfigurationManager.AppSettings[“ApplicationName”].ToString()
}

catch(Exception)
{
     throw new Exception(“There was an error obtaining the ApplicationName from configuration.”);
}

Now What Do We Know?

We know WHAT has happened.  We don’t know WHY or HOW we can fix it yet.  So, we still need to make this better. 

WHY should tell the user the reason for the situation.

Lets tweak it…

catch(Exception) { throw; }

catch(NullReferenceException)

{

     throw new ConfigurationErrorsException(“There was an error reading ApplicationName config value; the setting was not found.”);

}

As you can see, I have done a couple things here to clarify the error that is thrown. 

  1. I have singled out the situation where a NullReferenceException is thrown.  Ignore the low-hanging fruit here, this is just for sake of the example.
  2. I have changed the exception type to a ConfigurationErrorsException.  If this code is referenced elsewhere, we have a way to distinguish this exception from others.

Completing the Picture

So, we have the WHAT and the WHY taken care of, now we have to tackle the HOW.  This can be the hardest part of the puzzle.

HOW should tell the user what they can do to fix the problem. 

The hard part about this is not overdoing it.  You don’t need 3 paragraphs of possible solutions.  When more than one solution could exist, give the user the most likely resolution as a possible fix.

Lets tweak it…

catch(Exception ex)

{

   throw new ConfigurationErrorsException(String.Format(“An unexpected error occurred when trying to read the ApplicationName setting from configuration.  {0}”, ex.Message), ex);

}

catch(NullReferenceException)

{

   throw new ConfigurationErrorsException(“There was an error reading ApplicationName config value; the setting was not found.  To resolve this, add an appSetting to your configuration with valid string value.  This value cannot be empty.”);

}

Now we are looking much better.  There are two keys here…

  1. We expanded the first catch to let the user know that something happened that we were not expecting.  We explain what was being done and what the unexpected event was.  At this point, it is all the information we have.
  2. We expanded on our second exception and explained that the user can resolve the issue by adding the missing setting.

This gives us a WHAT, WHY, and HOW

Summing It Up

Good exception design is something that can go a long ways towards improving the user experience with your application.  I realize there is quite a bit of low-hanging fruit in this post but I ask that you look past it and understand the concept I am trying to convey.

Further Reading

If you are more concerned with general practices with throwing exceptions, check out a few of these resources.

I will be following up to this at a later point with some other general practices I like to follow.

kick it on DotNetKicks.com

  1. saviant posted this