If you've ever developed in C#, you've likely encountered a snippet like the one below:
if (config?.Settings is not null)
{
config.Settings.RetryPolicy = new ExponentialBackoffRetryPolicy();
}
This check is necessary because, if config
or config.Settings
is null
, a NullReferenceException
is thrown when trying to set the RetryPolicy
property.
But no more endless if
s! The latest version of C#, scheduled for release later this year with .NET 10, introduces the null-conditional assignment operators, which are designed to solve this exact issue.
Wait, Doesn't C# Already Have Null-Conditionals?
Yes! The null-conditional and null-coalescing operators have been around for a while. They simplify checking if a value is null
before assigning or reading it.
// Null-conditional (?.)
if (customer?.Profile is not null)
{
// Null-coalescing (??)
customer.Profile.Avatar = request.Avatar ?? "./default-avatar.jpg";
}
However, these operators only worked when reading a value, not setting one. With C# 14, you'll be able to use it on the left-hand side of the =
operator! Depending on your codebase, this can drastically clean up your code by reducing the number of if
statements needed to assign values.
Prerequisites
C# 14 is still in development. This means that the syntax might change slightly before the final release of .NET 10. However, I'll update this article if anything does change before the final release.
You'll need to download and install the latest .NET 10 preview SDK to run the snippets below. Next, create a new C# project (using a Console project in this article) and ensure it targets .NET 10. You'll also need to enable preview language features by opening your *.csproj
file and adding the <LangVersion>preview</LangVersion>
tag:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Add the LangVersion tag below. -->
<LangVersion>preview</LangVersion>
</PropertyGroup>
</Project>
You're all set to follow along with the snippets below.
Null-Conditional Assignments With Object Properties
The introduction illustrated a typical pattern you'll find in C# projects when assigning values to object properties: first check if the object itself is not null
. If you don't, a frustrating NullReferenceException
gets thrown, and those are no fun to debug.
Take the snippet you saw at the beginning of this article:
if (config?.Settings is not null)
{
config.Settings.RetryPolicy = new ExponentialBackoffRetryPolicy();
}
This becomes a one-liner in C# 14:
config?.Settings?.RetryPolicy = new ExponentialBackoffRetryPolicy();
Now, the compiler will first make sure that config?.Settings
is not null
before proceeding to assign the RetryPolicy
property. If config?.Settings
is null
, the assignment is skipped.
Null-Conditional Assignments With Indexer Elements
The new operator also works on indexers. This is useful when you're unsure whether a dictionary or list is null
before assigning a value.
Below is how you'd currently check if a dictionary is defined before assigning a value to it:
if (customerData is not null)
{
customerData["LastLogin"] = DateTime.UtcNow;
}
Again, this is much simpler in C# 14:
customerData?["LastLogin"] = DateTime.UtcNow;
Similar to the first example, the compiler will first check if properties is defined. If it is, the assignment is executed; otherwise, it's ignored.
Null-Conditional Assignments with Compound Assignments
You're not limited to just using standard =
assignments. You can also use the new operator with compound assignment operators like +=
and -=
.
Previously, you would have probably written something similar to the snippet below:
if (results is not null)
{
results.ItemsProcessed += 5;
}
This now becomes:
results?.ItemsProcessed += 5;
You can even combine it with other operators, like the null-coalescing assignment operator (??=
), to handle cases where the object exists but the property itself is null
.
customer?.Name ??= "Guest";
In the one-liner above, if customer
is null
, the entire assignment is skipped. Otherwise, if customer
is defined, but Name
is null
, then the property is assigned "Guest"
.
Things To Keep In Mind
Before applying null-conditional assignments throughout your codebase, keep these things in mind.
Side-Effect Prevention
When a null-conditional statement assignment is evaluated, the right-hand side of the expression is not executed unless the left-hand side is defined. This is to prevent side effects arising from the right-hand side executing, but then resulting value is being discarded when performing the assignment.
For example, you might have a function that generates the next ID in a sequence and returns it:
customer?.Id = GenerateNextCustomerId();
If customer
is null
, then GenerateNextCustomerId()
won't be executed. This is logical, since you don't want to unnecessarily increment your customer ID counter if the value isn't going to be used. Nevertheless, it's good to keep in mind when utilizing the operator.
Increment and Decrement Operators Aren't Supported
Sometimes it's convenient to increment a value using the ++
or --
operators. However, those won't work if you're using a null-conditional assignment operator.
For example, this code snippet will break:
// Will fail as ++ and -- are not supported with null-conditional assignments
customer?.TotalOrders++;
Why not support it? The C# design team decided that these operators would be more challenging to implement and might not yield the expected results. Hence, a decision was taken not to support it.
Don't Overuse It
This is my personal preference: avoid overusing the operator, as it might make it unclear why a specific value isn't being assigned.
For example, consider the snippet below:
customer?.Orders?.FirstOrDefault()?.OrderNumber = GenerateNewOrderNumber();
If a customer reports that the order number is not showing, you would need to look at this single line of code and determine:
- Did the
GenerateNewOrderNumber()
method returnnull
- Was
customer
set tonull
? Maybe the query to retrieve it is incorrect. - Was
customer.Orders
set tonull
? Maybe the ORM never attached the order list to the customer entity. - Was the
customer.Orders
list empty, soFirstOrDefault()
returned anull
value? - Was the first value in the
Orders
list anull
? This could indicate some deserialization issue somewhere.
It's too many "what ifs" for one line. So, if your code looks like that, seriously consider refactoring it to be easier to debug. Perhaps to something like this:
if (customer is null)
{
logger.LogWarning("Customer is null for {CustomerId}", customerId);
return;
}
var firstOrder = customer.Orders?.FirstOrDefault();
if (firstOrder is null)
{
logger.LogWarning("Could not find an order for customer {CustomerId}", customerId);
return;
}
firstOrder.OrderNumber = GenerateNewOrderNumber();
This code makes it much easier to determine why an order number was not assigned, simply by reviewing the application logs.
Conclusion
The new null-conditional assignment operator is a convenient feature coming in C# 14. It lets you avoid nesting assignments inside if
statements, making your code easier to read. However, keep in mind the limitation of not supporting ++
and --
operators, and try not to overuse the new operator.
This is just one of several enhancements being introduced with C# 14. When you do update your projects to .NET 10, which is the next LTS version of the SDK, consider how you can take advantage of these quality-of-life improvements to neaten your codebase.
Top comments (0)