Bonus of the Null-Propagation Operator

The Null Propagation Operator ?. has been a part of C# for almost 5 years. The feature was well-thought out and well received, and has become so prevalent in every code-base out there that I can't remember life before it.

So it was quite jarring to realise that I forgot how it works...

First, some revision. In C#, there are 2 main types of Types; classes and structs. If a variable is of a class type such as String, then the variable is actually a type-safe pointer to where the String can be found. If, however, a variable is of a struct type, then the variable is the actual data within the struct. Due to this, variables of classes can point to nothing and thus be null, while structs can not.

To give struct types the ability to be null, .NET 2 introduced the Nullable<T> type. This would simply be a wrapper around another type with 1 extra piece of information - whether it is pretending to be null. Nullable<T> is also a struct so it is important to remember that there are no null pointers involved, it's all just compiler magic to compare a Nullable<int> to null.

C# allows Nullable<int> to be written as int? in the same way that System.Int32 can be written as int i.e. they are the same thing. However since the former is so verbose while the latter is easy, it has been years since I wrote Nullable<int> instead of int?. I remember when the feature was first introduce, at the same time as my first internship, my team leader insisted that Nullable<int> was better because it is more explicit and easier to spot. I've forgotten that lesson... until now.

The Surprise

Today I came across code akin to the following, which adds a string representation of a DateTime? to a list.

const string format = "yyyy-MM-dd";
var list = new List<string>();
DateTime? date = null;

// SOME CODE HERE - value could be set or not

list.Add(date?.ToString(format));

I had to change the code so that if the date was null, it wouldn't be added to the list. Therefore, I made the change:

if (date != null)
{
    list.Add(date.ToString(format));
}

The above code didn't compile. It made me blink twice. Can you spot it?

Even after thousands of usages of Nullable<T>, C# has been designed in a way to make me forget it even exists, and today C# has managed. The code does not compile because Nullable<DateTime> does not have a ToString(string format) method; DateTime does. The fix is simple:

list.Add(date.Value.ToString(format))

However, the above is ugly. I think it gives me cognitive dissonance. It forces me to recognise that DateTime? is actually a complicated thing.

It also made me appreciate how easy it is to forget the utility of the Null-Propagation Operator and how different it is compared to the regular "dot". A.M() will always call a method M on A. However A?.M() might call M on a third class, B, if A is actually Nullable<B>. This special treatment of Nullable<T> is a hard-coded in Roslyn and that is why it was so surprising; special cases always are. Although I'll forgive it this time since it's just so useful.


Other posts you might like


Join the Discussion

You must be signed in to comment.

No user information will be stored on our site until you comment.