C# Nulls – Part 3 – Null Coalescing and Conditional

In Part 2 we covered the enabling C#’s nullable option to subtly change how we handle reference types.  Once enabled reference types are by default no longer [expected] to be nullable.  In this final part of the series, I will be covering the use of the Nullable Reference Type, Null-Conditional and Null-Coalescing operators that we briefly introduced in Part 1.

We previously discussed possible issues with legacy code when enabling the Null Reference Types feature as this changes a method’s contract. You may find it useful to enable against legacy code just to identify possible logic errors, however, depending on the size of your application and warnings issued you may not initially make it a permanent switch, as it could take a while to refactor your existing code.

Null Not Expected

Below, for the purposes of our example, is a call to method GetClientRecord() where the possibility of a null value is not being handled.  Without the #nullable enable directive the code compiles without any warnings.

With #nullable enable directive present the compiler will issue the following warning:

warning CS8603: Possible null reference return

And more conveniently, when working on the code, Visual Studio Code displays the following hint.  This is informing you that the method’s signature states it is not expected to return null.  It is not part of the contract.

Screenshot of C# warning of null not expected
Screenshot of C# warning of null not expected

Nullable Reference Type Operator

To inform the compiler that GetClientRecord can return null we need to update our method’s declaration to state that null is a possible result.  This is performed by using the ? postfix unitary operator on the reference type being returned.

However, after we’ve done this, we have new warnings appear where we are calling GetClientRecord().

Screenshot of C# warning of possible null to non-nullable conversion.
Screenshot of C# warning of possible null to non-nullable conversion.

Our declaration of the variable cr is a reference type of ClientRecord, which at first glance, looks like the same time as our function.  So, why the message?  

This is because the function is declared as ClientRecord? which means the value returned will either be null or point to an instance of a ClientRecord object.  However, this line of code is declaring cr as ClientRecord which means that we are only expecting cr to point to an instance of ClientRecord and is NOT expected to be null.

Declaring Reference Type Variables as Nullable

To resolve our last warning, again we use the ? postfix operator to indicate the variable can be nullable.  Both signatures now match.

So far, so good.  But we are still seeing a warning when using cr.Name.  This takes us to the benefit of enabling nullable reference types. 

Screenshot of C# warning of dereferencing a possible null reference.
Screenshot of C# warning of dereferencing a possible null reference.

We are now alerted to a possible incorrect, program breaking or bug-prone part of our code.  The compiler knows that cr may be null.  The compiler and Visual Studio’s hints are encouraging us (the developers) to be more explicit and cover all null eventualities now rather than having to frustratingly debug code in the future.

Null-Conditional and Null-Coalescing Operators

Below I have made a few changes to our main function.  For the purposes of our demonstration the call to GetClientRecord has been changed to pass a new value so null will be returned.

We have also introduced the use of the null-conditional operator (?.) and the null-coalescing operator (??). 

The null-conditional operator in cr?.Name evaluates as- if cr is set then use the value held in the property Name otherwise use the value null.

We could have just left it like that but for our demonstration, we have also thrown in the use of the null-coalescing operator (??).  This evaluates as – if the expression of the left evaluates to null then use the value on the right.

Alternatively, we could have written our code like this:

I have thrown in the use of both these operators for this example for two reasons.  Firstly, to highlight a design pattern you are likely to come across in wild, where at first looks, with question marks abound, it may not seem like the familiar C# you know.

Secondly, when considering to enable Nullable Reference Types in your projects, it may seem like this is going to cause you a lot more coding and be more time-consuming.  This example, shows, that in some circumstances the additional overhead may not be that much.  However, refactoring legacy code could be time-consuming and not commercially viable.  I do, however, strongly recommend enabling this feature on all new projects.

Null-Coalescing Assignment Operator

…And finally, a mention about strings and evaluation.  As strings are reference types the nullable reference type features apply here as well.  In the example below our ClientRecord type is not nullable, nor is the property Name.  However, NickName may be null.  Note the use of the Null-Coalescing Assignment Operator (??=). 

For both the Null-Coalescing (??) and Null-Coalescing Assignment (??=) operators, if the valuation on the left is not null then the expression on the right is NOT evaluated. The following code shows this in action.

The result from the above code is

Hi David
Hi *********

The first call to SayHello displays “David”.  This is because NickName is null and therefore GetAndThenChangeName() is called.  This returned “David” and sets the property value to “*********”.  The second call to SayHello outputs “*********” which was its newly assigned value.

If we were to uncomment the nickname assignment: –

The output will be

Hi Dave
Hi David

This is because Nickname was set and therefore GetAndChangeName() is never called.

The C# toolbox contains a number of operators to assist with handling nulls.  I listed many of these in Part 1 of this series.  If you have not already done so, please take a peek and make yourself more familiar with these.  The links will take you to the Microsoft documentation for a deeper understanding of each operator.

Leave a Reply

Your email address will not be published. Required fields are marked *