Friday, January 9, 2009

Exceptions 101: ArgumentNullException

Today's Exception is the ArgumentNullException. The Message for this exception will usually start out with "Value cannot be null", it will then be followed by the name of the parameter.


This specific exception is thrown by the .NET Framework (or by you, if you choose to) whenever a method is expecting a non-null instance of an object as a parameter, but receives a null value. This exception exists to prevent the NullReferenceException from being thrown, and to prevent other unpredictable, unstable (and otherwise nasty) actions from the methods that try to use the value, and the objects that may use them later. Imagine if you continued passing null to some obscure private method down in the depths of your application, and then suddenly received a NullReferenceException. There's a situation that may be even worse... What if your application continued running happily and never warned you that you had passed in a value that was null until 3 hours after it happens?! This is the kind of thing that can happen when ArgumentNullExceptions aren't thrown. Trust me, this exception is your friend.

So how do you debug an ArgumentNullException? Fortunately, the designers of .NET saw to it that most of the times they throw the ArgumentNullException, it's thrown from within a public method. Where it's not thrown in a public method, it's pretty close under the surface. This means, you don't usually have far to look. Typically, this information comes from a combination of the stack trace and the error message itself. Let's take a look at an example:




using System;

 

class Program

{

    static void Main(string[] args)

    {

        string value = null;

        TimeSpan timeSpan = TimeSpan.Parse(value);

        Console.WriteLine(timeSpan);

        Console.ReadLine();

    }

}




Now, in the code above, we're doing a very simple task; parsing a string to create a TimeSpan object. There's only one problem. The value we want to parse is null! Now, let's just pretend for a second that it isn't so obvious which value is null. (Pretend I got value from a database.) So, I get this string value back and I pass it into the TimeSpan.Parse static method. All the sudden I get this ArgumentNullException.

Let's take a look at the first few lines of the stack trace. To view the exception detail, click the little link on the exception window (like the one above) and a new modal window will appear with information about the exception. When you click the text area to the right of "StackTrace", you'll see a little down arrow that you click to open the exception detail. Your screen should now look like this:





Here's the first part of the stack trace:

at System.TimeSpan.StringParser.Parse(String s)
at System.TimeSpan.Parse(String s)
at Program.Main(String[] args) in ... (truncated for simplicity)


The top line of our stack trace is where this exception is getting thrown. Now let's take a look at the error message:


Value cannot be null.
Parameter name: s



So this is saying that the parameter s that was passed into the System.TimeSpan.StringParser.Parse method is null, and the method was expecting a non-null string value. Internally, StringParser.Parse calls a method that checks if s is null. If it is, then StringParser.Parse throws an ArgumentNullException. Without an actual string value, the Parse method isn't going to work properly, so it throws an exception.


Now, looking at the method that called System.TimeSpan.StringParser.Parse, we see that it only takes one parameter, of type string, called s, so we are probably safe to assume that we should check the parameter values that we pass into the method, to see if any of them are null. In this situation, we see that value is null, and we're safe to assume (in this situation) that our value property is the culprit. And indeed, if we change the string (to a string that can be parsed), then we get a proper result.

This exception should be thrown whenever you absolutely, positively, need a value to not be null, and someone's trying to get one past you. If at all possible, throw this exception in your public methods, as early as possible in the code execution.