Introduction
If you’ve read blog posts or even MSDN articles explaining new features in C# 6.0 – I’m sure you’ve learned that the null
-conditional operator in C# 6.0 will help you to greatly reduce the number of hard-to-debug and hard-to-reproduce NullReferenceException
-s.
When I first read about this operator, the only thing that registered in my mind was that it helped you to chain null
checks, especially for descending into data structures, and short-circuit the rest of the checks as soon as you hit null
somewhere in the chain. So, I thought it was just a mere convenient syntactic sugar.
But I still did not completely understand the true power of this operator. I’m usually pretty meticulous about null
checks and have a lot of C# 5.0 code like this:
private static int GetCurrentSpeed(Car car)
{
if (car != null && car.Engine != null && car.Engine.ControlUnit != null)
{
return car.Engine.ControlUnit.CurrentSpeed;
}
return 0;
}
And I was debating whether it was worth it to go through the code base and refactor it to use null
-conditional operator. Would I gain anything? Was it worth it just to have the code to look like this:
private static int GetCurrentSpeed(Car car)
{
return car?.Engine?.ControlUnit?.CurrentSpeed ?? 0;
}
Okay, I cut 5 lines of code, what’s the big deal?
One day I was watching Pluralsight course Exploring C# 6 with Jon Skeet (fast forward to 3:40) and I finally got the answer, which actually was in plain sight in the MSDN article:
Quote:
The new way is thread-safe because the compiler generates code to evaluate a property one time only, keeping the result in temporary variable.
As much as it became clear to me, I still wanted to see what’s going on. As they say – “A picture is worth thousand words”. What is the best picture? Of course the picture of IL code. So, I created these simple models:
namespace CS6
{
public class ControlUnit
{
public int CurrentSpeed { get; set; }
}
public class Engine
{
public ControlUnit ControlUnit { get; set; }
}
public class Car
{
public Engine Engine { get; set; }
}
}
and two console applications – one in VS2013 (C# 5.0) and another in VS2015 (C# 6.0), the ones that are shown in the previous code snippets. (Note: In VS2015, in project properties, you can specify what version of C# compiler to target). Then I used JetBrains dotPeek to see what IL code was created by each of the compilers.
IL Generated By C# 5.0
You can click on the image and see the code and IL side-by-side, but you might prefer a C# 5.0 Program and IL Gist.
If you look at the C# 5.0 code line 13, the code that accesses car.Engine
property, has corresponding get_Engine()
invocation (line 59 in IL). Similarly, code on the same line that accesses car.Engine.ControlUnit
property has corresponding get_Engine()
and get_ControlUnit()
invocations (lines 62 and 63 in IL).
The car.Engine.ControlUnit.CurrentSpeed
property access code on line 15 has corresponding invocations of get_Engine()
, get_ControlUnit()
and get_CurrentSpeed()
on lines 79 through 81.
The problem with this code is that at any time the thread it is running on can be preempted and another thread can assign a null
value to the previously checked property. When the control is yielded back to the original thread and you access that property – the hard-to-debug and hard-to-reproduce NullReferenceException
will be thrown.
IL Generated By C# 6.0
You can click on the image and see the code and IL side-by-side, but you might prefer a C# 6.0 Program and IL Gist.
If you look at the C# 6.0 code line 13, the code that uses null
-conditional operator to access three properties has only three corresponding calls to get_Engine()
, get_ControlUnit()
and get_CurrentSpeed()
(lines 64, 72 and 80 in IL) producing far more robust code.
Conclusion
We finally got an excellent feature to prevent NullReferenceException
in our code and you should move your code base to C# 6.0 as soon as possible!