Null-conditional assignment in C# 14
As the release of C# 14 is coming closer and most of the features are already available in preview, I decided to try them out myself. I started with null-conditional assignment, a syntactic sugar feature that can help you get rid of some null checks in your code.
In C#, accessing a member of a null
value throws a NullReferenceException
:
Report? report = null;
Should.Throw<NullReferenceException>(() => report.ItemsProcessed);
Likewise, accessing an element of a null
value throws a NullReferenceException
:
List<int>? list = null;
Should.Throw<NullReferenceException>(() => list[0]);
C# 6 introduced a null conditional operator which returns null
instead of throwing a NullReferenceException
in such cases. Thanks to it, you don't need explicit null checks anymore when accessing members and elements of nullable types:
Report? report = null;
(report?.ItemsProcessed).ShouldBeNull();
List<int>? list = null;
(list?[0]).ShouldBeNull();
However, null-conditional operator couldn't be used on the left-hand side of an assignment operator. The only way to prevent a NullReferenceException
when assigning to a potentially null
value was to put the assignment in an if
block:
Processor processor = new();
Report? report = null;
if (report != null)
{
report.ItemsProcessed = processor.Process();
}
The null-conditional assignment introduced in C# 14 removes this limitation and allows the use of the null-conditional operator also on the left-hand side of an assignment operator:
Processor processor = new();
Report? report = null;
report?.ItemsProcessed = processor.Process();
Such a use of a null-conditional operator is equivalent to the previous example with the if
block. This means that the expression on the right-hand side of the assignment operator will not execute if the left-hand side evaluates to null
:
processor.Processed.ShouldBe(false);
If you think about it, you are likely to consider this expected behavior. However, it is much easier to overlook the null-conditional operator on the left-hand side of an assignment expression than it is to overlook that a line of code is inside an if
block. Hence, some caution is advised when using null-conditional assignment if the expression on the right-hand side has side effects.
Null-conditional assignment works the same for element access, not just for member access:
Processor processor = new();
List<int>? list = null;
list?[0] = processor.Process();
And it can also be used with all types of compound assignment operators:
Processor processor = new();
Report? report = null;
report?.ItemsProcessed += processor.Process();
You can try the feature yourself if you have the latest version (17.14.9) of Visual Studio 2022 installed. I couldn't find that documented anywhere, but it doesn't seem to rely on .NET 10 in any way. It works just as well in .NET 9.
However, to get access to the feature in your code, you need to set the language version in your project file to preview:
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
When you do that, you will even get a recommendation in Visual Studio when it recognizes that you can take advantage of this feature:
You can find a sample project showcasing this new feature in my GitHub repository. It includes several tests with different code snippets: some that worked already before C# 14 and some that only work with C# 14. Feel free to try it out if you're not in the mood of writing sample code yourself.
Null-conditional assignment adds support for additional uses of null-conditional operator which weren't allowed before. Although it's just syntactic sugar, it can make your code more concise and easier to read in certain cases. Just don't forget that the expression on the right-hand side doesn't execute if the left-hand side evaluates to null.