Twitter GitHub Facebook Instagram dirv.me

Daniel Irvine on building software

Null parameter values are evil

17 July 2010

One of the ideas I suggested in my last post was, What if C#:

prohibited passing null as method parameters
In this post I’ll discuss why I feel it’s a good thing, and why it’s entirely feasible to do.

So what the hell am I talking about when I say null parameter values are evil?  First up, I’m not saying nulls are evil.  Sir Tony Hoare famously called null references the billion dollar mistake.  He was talking about historically bad ideas at the time, and believes that we’d all be better off if “null” never existed.  I’m not convinced.  I feel nulls have a place and a special meaning, just like void has a meaning.

So I’m focusing on a smaller idea: null parameter passing.  I consider this an anti-pattern:

R Method(A a, B b)
{
    if(a == null) throw ArgumentNullException("a");
    if(a == null) throw ArgumentNullException("b");

    // ...
}
 
There are a few reasons why I’m not happy with this.
  • We rely on human code reviews for ensuring this (anti)pattern is always followed.  That means there’s a risk it’ll be missed.
  • It’s boilerplate.  Our code would be much cleaner and easier to read if we didn’t have to follow this.
  • This is just one way of a variety for null checking: for example, we can also use
    Debug.Assert(a != null, String.Format(Messages.CannotBeNull, "a"));>
     

So I’m suggesting we move the job of generating this code to the compiler, so that the above example code  can now be written as

R Method(A a, B b)
{
    // a and b are definitely not null if this point is reached</pre>
    // ...
}
Calling code looks like this:
a = null;
b = null; 
var r = obj.Method(a, b);  // throws an ArgumentNullException</pre>

So, it’s technically possible--but the remaining question is this: are there any valid use cases for null method parameter values?  The answer as far as I can tell is no.  The most obvious use for null parameters is with method overloading:

>R Method(A a, B b)
{
    // do stuff
}

R Method(A a)
{
    return Method(a, null);
}

This can be refactored as:

R Method(A a, B b)
{
    DoSomethingWithA(a);
    DoSomethingWithB(b);
    DoSomethingWithAandB(a, b);
}

R Method(A a)
{
   DoSomethingWithA(a);
   // since a is passed in alone, we can assume that its value
   // is independent of B, so DoSomethingWithAandB() does not
   // need to be called, or can be called with b's previous
   // value. 
}
 

This is actually a neater design anyway, because it requires splitting out the independent processing of the variables.  I’ve come across one situation that’s a bit ugly: when you need to call 3rd party libraries that accept and expect null parameters.  For example:

public R OtherMethod(T t)
{
  _thirdPartyLibrary.OtherMethod(t);   // what to do if null is a valid value?
}

This is especially difficult if the connection to the third party library requires set up and disposing, for example:

public R OtherMethod(T t)
{
    using(var thirdPartyLibrary = new ThirdPartyLibrary())
    {
        thirdPartyLibrary.OtherMethod(t);
    }
}

This would necessarily become:

public R OtherMethod(T t)
{
    using(var thirdPartyLibrary = new ThirdPartyLibrary())
    {
        thirdPartyLibrary.OtherMethod(t);
    }
}

// this overload is used for null values
public R OtherMethod()
{
    using( var thirdPartyLibrary = new ThirdPartyLibrary())
    {
        thirdPartyLibrary.OtherMethod(null);   // passing null is fine
                                               // because this component
                                               // wasn't built with our
                                               // amazing compilation technique
    }
}

Yes, it’s our old friend repeated code - but I’d happily put up with this for the sake of the extra precondition checking I’d get for free.

How to implement this is the next question: one option would be a form of IL injection, like aspect-oriented programming.  Another option would be an entirely new compiler, with not just this language modification, but all the other changes I suggested in my previous post.   That would be a cool research project!

If you’re still following along, there are a couple of obvious questions that come up at this point:

  1. What about .NET 4 code contracts?
  2. What about F#?
Both valid points, and yes both solve this issue, but unfortunately I don’t like either solution.  Code contracts is again more boilerplate code - it just shifts the form of the code written.  I believe that for software engineering to take the next step, we need to move towards less code and more component-based design.  F#, on the other hand, solves it by dissallowing nulls entirely, but I think F# is an ugly language.  Spec# was on the right track, but that’s really what code contracts have become in C#.

I’m always interested in projects making the leap to non-nullable types.  I think null really helps, in particular returning null from methods, either as the return value or an out/ref parameter.  The TryGetValue pattern is an awesome example of this.  It’s a useful idiom because you can do a Has and a Get in one atomic operation.  You need null in this case--it just doesn’t make sense to return any value other than null, if Has is false.  Of course, proponents of non-nullable types would argue that there are other ways of achieving the same thing--but that’s a discussion for another day...

About the author

Daniel Irvine is a software craftsman at 8th Light, based in London. These days he prefers to code in Clojure and Ruby, despite having been a C++ and C# developer for the majority of his career.

For a longer bio please see danielirvine.com. To contact Daniel, send a tweet to @d_ir or use the comments section below.

Twitter GitHub Facebook Instagram dirv.me