Friday, 21 November 2008

Null Reference checking, functional style

I love Extension methods. I only wish I could pull one out of my computer, so that I could give it a great big hug. Anyway ... That little outburst was prompted by the way that extension methods helped me out with an elegant solution when I needed to do a null reference check on a variable.

We've all written code like

variable = SomeMethodThatMightReturnNull();

if (variable == null) 
{
	throw new ExceptionIndicatingTheNullness();
}

which is alright, but don't you think there are too many curly braces there? You don't? Well, I suppose there are only two, but wouldn't it be more elegant if there were none?

I've written before about a nice feature of extension methods: because they are effectively static methods you can call one on a null reference, and it doesn't blow up. Instead it just passes the null value through into the method.

So we can create this:

public static class FluentMethodExtensions
{
    public static T EnsureNotNull<T>(this T instance, Func<Exception> exceptionBuilder) where T : class
    {
        if (instance == null)
        {
            throw exceptionBuilder();
        }
        return instance;
    }
}

(Note the where constraint on the method (line 3): we need that so that we can do the null comparison in line 5.)

With that in place we can zap those curly braces:

variable = SomeMethodThatMightReturnNull().EnsureNotNull(() => new ExceptionIndicatingTheNullness());

How about that then?

Of course, the usual reason for throwing your own custom exception is so that you can include helpful details about the problem. In that case you might want to create some overloads to EnsureNotNull. How about this one, intended to allow you to pass through parameters so that you can build them into a message using string.Format:

public static T EnsureNotNull<T>(this T instance, Func<object[], Exception> exceptionBuilder, params object[] exceptionMessageParameters) where T : class
{
    if (instance == null)
    {
        throw exceptionBuilder(exceptionMessageParameters);
    }

    return instance;
}

You use it like so:

// ...
Range range = GetRange(workbook, rangeName)
                .EnsureNotNull(BuildInvalidRangeNameException, rangeName);
// ...

private ExcelException BuildInvalidRangeNameException(object[] exceptionParameters)
{
    return new ExcelException(string.Format("Named range {0} does not exist.", exceptionParameters));
}

You can create your own. Go on. Try it. It's fun.

0 comments:

Post a comment