What Is the Null-Conditional Operator in C#?

The null-conditional operator in C# provides a concise, thread-safe way to access members and elements of an object only when that object is not null. Introduced in C# 6.0, it prevents the common NullReferenceException by automatically checking for null values before proceeding with member access or method invocation. This article explains the purpose of the null-conditional operator, how its syntax works for member and index access, and how it simplifies conditional logic when combined with related operators.

The Purpose of the Operator

In software development, dereferencing a variable that holds a null reference results in a runtime crash via a NullReferenceException. Traditionally, developers had to write verbose defensive code using nested if statements to ensure each reference in an object hierarchy was valid before accessing its properties.

The null-conditional operator (?. and ?[]) was designed to eliminate boilerplate null checks. Its primary purpose is to short-circuit operations: if the operand evaluates to null, the entire expression returns null immediately without throwing an exception or evaluating the rest of the chain.

Syntax and Core Variants

C# provides two forms of the null-conditional operator:

  1. Member Access (?.): Used to access properties, fields, or methods.

    string? city = customer?.Address?.City;

    If customer is null, evaluation stops, and city is assigned null. If customer is valid but Address is null, evaluation stops there. The runtime only attempts to read City if both preceding objects are instantiated.

  2. Element Access (?[]): Used to access indexers, collections, or arrays.

    int? firstScore = scores?[0];

    If the scores collection is null, the expression evaluates to null instead of throwing an exception.

Short-Circuit Evaluation

The null-conditional operator relies on short-circuit evaluation. If the left-hand side of ?. resolves to null, no subsequent member accesses or method calls in that chain are executed.

int? length = user?.GetProfile()?.DisplayName?.Length;

In this example, if user is null, GetProfile() is never invoked, avoiding unnecessary processing and preventing exceptions.

Thread-Safe Event Invocation

A critical use case for the null-conditional operator is triggering events. Previously, raising an event required assigning it to a temporary variable to prevent race conditions where a subscriber might unsubscribe between the null check and the invocation:

// Traditional approach
var handler = OrderCompleted;
if (handler != null)
{
    handler(this, EventArgs.Empty);
}

With the null-conditional operator, this pattern is reduced to a single thread-safe line:

OrderCompleted?.Invoke(this, EventArgs.Empty);

The left operand is evaluated only once, eliminating the race condition.

Pairing with the Null-Coalescing Operator

Because expressions using ?. return a nullable type (such as int? instead of int), they are frequently paired with the null-coalescing operator (??) to supply a fallback default value when a null reference is encountered.

// Returns the string length if valid, or 0 if user or Name is null
int nameLength = user?.Name?.Length ?? 0;

By substituting bulky conditional blocks with expressive inline operators, the null-conditional operator minimizes boilerplate code, improves readability, and makes C# applications significantly more resilient against null-pointer errors.