On accessing chains of potentially null properties
Raise your hand if you’ve ever written code looking like this:
var result = default(int);
if (fubar != null) {
var foo = fubar.Foo;
if (foo != null) {
var bar = foo.Bar;
if (bar != null) {
result = bar.Baz;
}
}
}
We’ve all done that, and it’s just sad. There seems to be a law of the Universe that says that if your code looks like a wedge, like the code above, then something is wrong with that code, or with the language it’s expressed in. It illustrates one of the reasons why Tony Hoare, inventor of Quicksort, called the null reference his “billion-dollar mistake”.
One can mitigate this to a degree, and use ternary operators, to make the code a little bit less horrible (but not that much):
var foo = fubar == null ? null : fubar.Foo;
var bar = foo == null ? null : foo.Bar;
var result = bar == null ? default(int) : bar.Baz;
Or maybe you’ve just given up, just call
fubar.Foo.Bar.Baz
and accept the null ref exceptions. Why don’t you also wear sweat pants while you’re at it?
Of course, the more theoretically-oriented of you will have immediately objected that for our code to attempt to reach deep into the object graph and grab Baz is in direct violation of the Law of Demeter. Right. But in reality, we deal with DOMs, JSON payloads, and other deep graphs that are designed to be traversed, all the time, and it would be impractical to be too dogmatic about this.
Ideally, we would have language support for this, and it seems like we will, in the next version of C#:
fubar?.Foo?.Bar?.Baz
In the meantime, what can we do?
Don’t we have a language construct that enables us to express code without immediately executing it? Well, yes we do: Lambdas and expression trees!
I’ve built a little helper that takes an expression tree, and evaluates it on an object, but doing null checks on the way. Using it is as simple as this:
var result = fubar.Get(f => f.Foo.Bar.Baz);
If you don’t even know if the fubar object is null, you can use it as a static method:
var result = NotNull.Get(fubar, f => f.Foo.Bar.Baz);
Here is the source code for the helper:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace Bleroy.Helpers {
public static class NotNull {
public static TProp Get<TSource, TProp>(
this TSource source, Expression<Func<TSource, TProp>> property)
where TSource : class {
if (source == null) return default(TProp);
var current = property.Body;
var properties = new List<PropertyInfo>();
while (!(current is ParameterExpression)) {
var memberExpression = current as MemberExpression;
if (memberExpression == null || !(memberExpression.Member is PropertyInfo)) {
throw new InvalidOperationException("All members in the expression must be properties.");
}
properties.Add((PropertyInfo) memberExpression.Member);
current = memberExpression.Expression;
}
properties.Reverse();
object currentValue = source;
foreach (var propertyInfo in properties) {
if (currentValue == null) return default(TProp);
currentValue = propertyInfo.GetValue(currentValue);
}
return (TProp) currentValue;
}
}
}
I’ve also posted this on Github as a Gist:
https://gist.github.com/bleroy/11131888
Of course, the code in this post comes with no guarantee of anything whatsoever, especially performance and I don’t recommend anybody use it. It’s a fun experiment, is what it is. I’d love to read your thoughts about it in the comments section.
Note: in Clay, we’ve copied Ruby’s Nil behavior, and it is possible to safely access deep properties without caring too much about them being null. This is nice, but doesn’t help you much if you’re using regular C# objects.
Update: I'm being pointed to Monads.Net, that is a set of monad-driven helpers, one of which addresses the same problem with a similar solution. With Monads.Net, you'd write:
var result = fubar.With(f => f.Foo).With(f => f.Bar).With(f => f.Baz);
I prefer my version, because I don't need to use one method call and one Lambda per property, but can instead use one big deep Lambda. It's nice too see others come to the same kind of solution however.
Update 2: Ian Griffiths was there years ago: his version rewrites the expression tree.