Null parameters in extension methods

There is the question of how to handle null parameters in extension methods. Consider this extension method that returns the number of digits in a string.

    public static class MyExtensions
    {
        public static int CountDigits(this string input)
        {
            var count = 0;
            foreach (var c in input)
            {
                if (char.IsDigit(c))
                {
                    ++count;
                }
            }
            return count;
        }
    }

Understand, this is just an example. I realize that I can replace all that code with a single function call, and in fact I wouldn’t actually write such a simple extension method to replace that one line of code in my program. Bear with me.

After running the code below, result will be equal to 10.

    string foo = "(555)123-4567";
    var result = foo.CountDigits();  // returns 10

So what should happen if I call that method when foo == null? Let’s see what the Framework does when I call a string method on a null reference.

    string foo = null;
    string lowerFoo = foo.ToLower();

Not surprisingly, that throws NullReferenceException. And, not surprisingly, the CountDigits method also throws NullReferenceException when foo == null. The difference, though, is important. If you look at the exception detail you’ll find that the call to foo.ToLower reports the exception as being thrown on that line. The stack trace for the call to CountDigits looks like this.

System.NullReferenceException was unhandled
  HResult=-2147467261
  Message=Object reference not set to an instance of an object.
  Source=sotesto
  StackTrace:
       at Test.MyExtensions.CountDigits(String input) in c:\Dev\Jim\Testo2012\sotesto\Program.cs:line 37
       at Test.Program.DoStuff() in c:\Dev\Jim\Testo2012\sotesto\Program.cs:line 28

The exception is thrown on the line containing the foreach statement.

If you understand how extension methods work, this shouldn’t be surprising. After all, the code foo.CountDigits(); is really just shorthand for MyExtensions.CountDigits(foo);.

The exception is saying that the error exists in the extension method when in reality the error exists at the call site. This is an important distinction. Imagine that you’re calling an extension method that exists in a library for which you have no source. If that extension method throws NullReferenceException, you will likely assume that the error is in the extension method. And nothing in the stack trace will tell you otherwise. There is nothing to indicate that it threw that exception because you passed a null parameter.

That is incorrect behavior.

Don’t believe me? Let’s see what happens if I rewrite that extension method.

    public static class MyExtensions
    {
        public static int CountDigits(this string input)
        {
            return input.Count(char.IsDigit);
        }
    }

Yes, that duplicates the functionality of the previous version. But if you call it with a null parameter, the result is quite different.

System.ArgumentNullException was unhandled
  HResult=-2147467261
  Message=Value cannot be null.
Parameter name: source
  Source=System.Core
  ParamName=source
  StackTrace:
       at System.Linq.Enumerable.Count[TSource](IEnumerable`1 source, Func`2 predicate)
       at Test.MyExtensions.CountDigits(String input) in c:\Dev\Jim\Testo2012\sotesto\Program.cs:line 36
       at Test.Program.DoStuff() in c:\Dev\Jim\Testo2012\sotesto\Program.cs:line 28

Here we see that the Enumerable.Count extension method threw ArgumentNullException, and actually told me why: the source parameter cannot be null. But, again, it looks like the error is due to the CountDigits extension method passing a null argument.

The answer to the question of what to do with null parameters to extension methods is simple: check them and throw ArgumentNullException.

    public static int CountDigits(this string input)
    {
        if (input == null)
        {
            throw new ArgumentNullException("input", "Value cannot be null.");
        }
        return input.Count(char.IsDigit);
    }

Now you know exactly why the error occured: the code that called CountDigits passed a null parameter.

The above is how the Framework methods handle null parameters. It’s considered a best practice. More importantly, this is the only way to know for sure where the real error lies.

1 comment to Null parameters in extension methods

  • Or you might even think a moment and decide that the correct thing to do with a null string is report that it contains 0 digits. (This kind of thing — logic at the boundaries of the specification — came up all the time when I was teaching Prolog, a logic-based programming language.)

Categories

A sample text widget

Etiam pulvinar consectetur dolor sed malesuada. Ut convallis euismod dolor nec pretium. Nunc ut tristique massa.

Nam sodales mi vitae dolor ullamcorper et vulputate enim accumsan. Morbi orci magna, tincidunt vitae molestie nec, molestie at mi. Nulla nulla lorem, suscipit in posuere in, interdum non magna.