Search
Close this search box.

C#/.NET Little Wonders: Null Conditional Operator in C# 6

Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.

Visual Studio 2015 is on the horizon!  In fact, some of you may already have played with the preview and seen some of the many neat new things to come – both in the IDE and in the C# language.

Note: All of the C# 6 features mentioned are current with the latest CTP of VS2015 as of the time of this writing.  The syntax and exact feature behaviors may be subject to change in the final released version.

Too many layers of null checks!

How many times have you had to consume a web service, and that web service has had many layers of nested objects, each of which can potentially be null

Often times, to be safe, this makes us end up having to write logic like this:

var response = someProxy.SomeWebMethod();

// so many layers, so many potential nulls...
if (response != null && response.Results != null &&
    response.Results.Status == Status.Success) {
  Console.WriteLine("We were successful!");
}

And this is just a status buried down two layers, imagine if the model was four, five, or six layers deep!

This happens a lot in C# (and Java) where at any step along the way of a deeply-nested model, you could encounter a null.  Indeed it would be nice to be able to have an option to cascade the null down the chain if there’s a null along the way.

Now, we get this new feature in C# 6, the null conditional operator.  This operator does exactly what we’re talking about: if the left side of the operator is null, it cascades a null of the resulting type to the right. 

In short, if you replace your “.” with the null conditional operator “?.”it will cascade the null down the line:

var response = someProxy.SomeWebMethod();

// so much better!
if (response?.Results?.Status == Status.Success) {
  Console.WriteLine("We were successful!");
}

So, if response is null, or if response.Results is null, then the result will be a null matching the return type of Status.  If not, you will get the actual value of Status.

Now, notice that we have “?.” in there twice, this is because we need to use the “?.” everywhere that we want to cascade the null otherwise a null will throw as expected.

For example, consider the following four similar conditionals:

    // #1
    if (response.Results.Status == Status.Success) { }
     
    // #2
    if (response?.Results.Status == Status.Success) { }
     
    // #3
    if (response.Results?.Status == Status.Success) { }
     
   // #4
   if (response?.Results?.Status == Status.Success) { }

Let’s look at each of these in turn:

  1. The first will throw if either response or response.Results are null 
  2. The second will throw onlyif response.Results is null, but will cascade if response is null
  3. The third will throw if response is null, but will cascade if response.Results is null
  4. The fourth will cascade if either response or response.Results are null

Most of the time, unless you know an element cannot be null in a chain (like if it’s a struct), you’ll want to continue to cascade down an identifier chain once you start.

What does null-cascading with a value type result do?

Let’s say that Results has a Count field that evaluates to an int, what would happen if we did this?

// what is the type of the LHS?
2 if (response?.Results?.Count > 0) {
  Console.WriteLine("We got data!");
}

The above does compile.  This is because C# is smart enough to realize that the end result of the chain is a value type, so it converts it to a Nullable<int> instead.  This means we may be comparing null to zero.  However, if you study the way nullable math works in C# (much like in SQL) you’ll find that null > 0 is false (incidentally so is null < 0).  For more details, see this post on Nullable Math Doesn’t Always Add Up.

Or, let’s do this instead:

// won't compile, will complain that can't implicitly convert
// Nullable<int> to int
int count = response?.Results?.Count;

You’ll see right away that C# considers the end result of that identifier chain to be Nullalble<int> (also referred to as int?) instead of int.

Want a default other than null? 

What if you want to cascade the null, but want to use another default value instead.  For example, take the Count expression above where it gave us a Nullable<int> that can cause some interesting math comparisons if the value is null, it would be better if we could cascade to something like zero.

Well, you can combine the null conditional operator with the null-coalescing operator (??) to provide a different default on null.

    // This will compile, if the expression evaluate to null then 0 will be used.
    int count = response?.Results?.Count ?? 0;

Indeed, notice that now we can type the value of count to int since null is no longer an option.

What if we’re dealing with an indexer property?

So, you may have noticed we’ve been dealing directly with members accessed using the dot (“.”) operator, what happens if we want to use an indexer property (“[]”)?

For example, let’s say that Results contained a List<Address> called Addresses. And List<T> has an indexer property.

We don’t want to have to go back to:

// notice the [...] indexer on addresses
if (response != null && response.Results != null &&
    response.Results.Addresses != null &&
    response.Results.Addresses[0] != null &&
    response.Results.Addresses[0].Zip == "63368") {
  Console.WriteLine("Are we neighbors?");
}

Notice the indexer on Addresses, so we can’t use “?.“ because indexers use a different syntax than fields, properties, and methods.  Fortunately, C# provides a null conditional form of the indexer operator “?[]” which works just like the standard member-access null conditional operator, but for indexers instead.

So now, we can write:

// Again, much cleaner...
if (response?.Results?.Addresses?[0]?.Zip == "63368") {
  Console.WriteLine("Are we neighbors?");
}

With this, if responseresponse.Resultsresponse.Results.Addresses, or response.Results.Addresses[0]are null, we will coalesce a null as the final result.

But wait, it works for more than just fields and properties…

The null conditional operator is not simply for fields and properties, it can be used for method calls as well.  Once again, if the identifier chain on the left evaluates to null the method will not be called and a null matching the return type (if any) will be cascaded along.

For example, how often do you have code like this:

if (x != null) {
  x.Dispose();
}

This is a fairly common occurrence, we want to call a function if the instance is not null, but it takes 4 lines of code to write that (assuming we do our full set of curlies).

Now, however, this can be simplified to one line:

    // a great one-liner!
    x?.Dispose();

Much cleaner!  Especially if you are doing a lot of these kind of statements in a larger cleanup operation; just imagine the lines you will save!

And what about processing events?  Those of you who have done any event raising know that there’s a standard pattern for raising an event where you take a copy of the handler, and then check the copy for null.  This is so that the value of the handler doesn’t change between the time you check and the time you invoke it.  We don’t want to lock the invocation, because that could be a very expensive lock if the subscribers do anything heavy.

// to avoid locking, yet be thread-safe, must make a copy of the delegate
// and then invoke the copy.
var copy = OnMyEvent;
if (copy != null) {
  copy(this, new EventArgs());
}

For more details on this pattern, see Safely and Efficiently Raising Events

Now with the null conditional operator, we can do this instead:

// again, reduced to a great one-liner:
OnMyEvent?.Invoke(this, new EventArgs());

So, the ?. will evaluate first and make a copy of the reference (hence satisfying the copy pattern) and then based on whether that is null or not, it will invoke the delegate.

Summary

The null conditional operator is a great code-reducer to eliminate a lot of very verbose null-checking logic where cascading makes sense.  Keep in mind that a null-conditional chain that results in a value type will create a Nullable<T> instead, and particularly pay attention when you try to perform arithmetic or logical comparisons versus null.

Thanks for reading my Little Wonders of C# 6 series, I’ll be back soon with more Little Wonders and Little Puzzlers in the weeks to come.

This article is part of the GWB Archives. Original Author: James Michael Hare

Related Posts