The other day, a friend and I were reviewing some code like this and decided we deeply disliked it.
// var line1 = person.Address.Lines.Line1 ?? string.Empty;
// throws NullReferenceException:
// {"Object reference not set to an instance of an object."}
// The ugly alternative
var line1 = person.Address == null
? "n/a"
: person.Address.Lines == null
? "n/a"
: person.Address.Lines.Line1;
If you have ever written code like that or even worse, with the if (obj != null) statements, then you can certainly relate to our lack of love for the constructs required to avoid the dratted NullReferenceException.
So we experimented with a solution and nearly got there. Tonight I finished that up and the new Dis class is born with the OrDat<T> method so you can now write this instead.
var line2 = Dis.OrDat<string>(() => person.Address.Lines.Line2, "n/a");
Here’s the full example and if you’re patient enough to scroll down, you’ll also find the full implementation of the Dis.OrDat class and method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DeepNullCoalescence
{
class Program
{
static void Main(string[] args)
{
var person = new Person();
// var line1 = person.Address.Lines.Line1 ?? string.Empty;
// throws NullReferenceException:
// {"Object reference not set to an instance of an object."}
// The ugly alternative
var line1 = person.Address == null
? "n/a"
: person.Address.Lines == null
? "n/a"
: person.Address.Lines.Line1;
// A cooler alternative
var line2 = Dis.OrDat<string>(() => person.Address.Lines.Line2, "n/a");
Console.WriteLine(line1);
Console.WriteLine(line2);
Console.ReadLine();
}
}
internal class Person
{
public Address Address { get; set; }
}
internal class Address
{
public AddressLines Lines { get; set; }
public string State { get; set; }
}
internal class AddressLines
{
public string Line1 { get; set; }
public string Line2 { get; set; }
}
}
And for the patient reader, here is the actual magic.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace DeepNullCoalescence
{
public static class Dis
{
public static T OrDat<T>(Expression<Func<T>> expr, T dat)
{
try
{
var func = expr.Compile();
var result = func.Invoke();
return result ?? dat; //now we can coalesce
}
catch (NullReferenceException)
{
return dat;
}
}
}
}