You've likely bumped into the pesky NullReferenceException
if you've developed anything in C#. This exception usually happens when you try to access a member of a variable that contains a null reference, disrupting the flow of your application.
Fortunately, C# offers some handy tools to help you navigate these null reference issues: the null-conditional and null-coalescing operators. These operators provide ways to handle potential null values gracefully while keeping your code clean and readable.
In this article, you will learn precisely how each operator works, when they should be used, and code samples demonstrating them in action.
What is the Null-Conditional Operator?
The null-conditional operator (?.
) is a unary operator in C# that checks if a variable is null before accessing its members. If the variable is null, the expression short-circuits and evaluates to null, preventing a NullReferenceException
from being thrown.
Short-circuiting means that if any part of a chained expression evaluates to null, the remaining parts are skipped, and the entire expression evaluates to null.
For example, if
customer?.ContactDetails
is null in the expressioncustomer?.ContactDetails?.Email?.Work
, thenEmail?.Work
is never evaluated, and the expression returns null.
This operator is handy when retrieving values from deeply nested properties in an object. For example, suppose you have an object with nested properties. You can use the null-conditional operator to safely access properties deep within the object without explicitly checking for null at each level. This makes your code more concise and readable, especially when dealing with complex data structures.
// Without null-conditional operator
string? name = null;
if (customer != null && customer.Address != null)
{
name = customer.Address.StreetName;
}
// With null-conditional operator
var? name = customer?.Address?.StreetName;
In the example above, the second statement using the null-conditional operator achieves the same result as the first, but in a much more succinct way. If either customer
or Address
is null, the expression will evaluate to null, preventing a potential NullReferenceException
.
A word of caution: while the null-conditional operator is quite helpful, you should only use it when it makes sense for a property to be null. Otherwise, you might run into some nasty surprises. For example, in the code snippet below, you already know customer
is not null, so it's unnecessary to use the operator on that variable:
// Previously in the same code block:
if (customer is null) throw new CustomerNotFoundException();
// With null-conditional operator on everything (bad)
var streetNumber = customer?.Address?.Street?.Number;
// With null-conditional operator on nullable properties (good)
var streetNumber = customer.Address?.Street?.Number;
While the example above might be harmless, there are cases where it can lead to strange bugs. For example, an expression might be evaluated as null, but it could be caused by a property in the expression chain you don't expect.
What is the Null-Coalescing Operator?
The null-coalescing operator is a binary operator that offers a concise method to manage null values. If the left operand if it is not null, it returns that; otherwise, it returns the right operand.
The syntax looks like this:
result = value1 ?? value2;
In this case, if value1
is not null, the result will be assigned the value of value1
. However, if value1
is null, the result will be assigned the value of value2
.
This is useful when providing a default value for a nullable variable. For example, you might want to display a default value if a customer's address is a null string:
var customerAddress = customer?.Address;
var displayAddress = customerAddress ?? "Unknown Address";
If the customer's address is specified, it'll be assigned to displayAddress
. Otherwise, "Unknown Address"
will be assigned to the variable.
You can even use the null-coalesce operator with the null-conditional operator shown above. For example, the above snippet could be combined into a single line like this:
var displayAddress = customer?.Address ?? "Unknown Address";
Now, if the customer
variable is null, the null-conditional operator will short-circuit the expression and return null. However, since we have a null-coalesce operator in the same line, "Unknown Address"
is assigned to the displayAddress
variable instead.
You can also use the null-coalescing operator in an assignment operation (??=
). This is useful if you have an existing variable and only want to assign a value if the current value is null. The snippet below demonstrates this:
var customerName = customer?.Name;
// Without null-coalesce assignment operator
customerName = customerName ?? "No name provided";
// With null-coalesce assignment operator
customerName ??= "No name provided";
Like the null-conditional operator, only use this operator when it makes sense. It's easy to fall into the habit of blindly assigning default values without considering whether they make sense. Sometimes a null is the best option:
// Here we assign "0" if a street number is not specified in
// an address... but does that make sense?
address.StreetNumber = request.Address.StreetNumber ?? "0";
In the example above, a default value of "0"
is assigned to StreetNumber
if none is specified in the request object. However, this can be confusing. Imagine that the address is used for a delivery, and the driver mistakenly gets lost looking for a house with street number 0. In this case, it's probably better to leave it blank or null if it's not applicable.
Conclusion
You've undoubtedly bumped into NullReferenceExceptions
, and they can be frustrating. Fortunately, C# provides operators specifically designed to streamline null handling and prevent those pesky exceptions from being thrown.
The null-conditional operator (?.
) lets you safely access members of potentially null objects, while the null-coalescing operator (??
) provides a concise way to supply default values when encountering nulls. Understanding and using these operators appropriately helps you write clear, robust C# code that gracefully handles null values.
Top comments (0)