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 Type
s; 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.
Join the Discussion
You must be signed in to comment.