C# Null Conditional Operator

C# conditionals

In C# programming, handlingnullChecking values is a common but error-prone task.

traditionalnullThe checking code is often verbose and tedious, and the one introduced by C# 6.0Null conditional operator(?.and?[]) greatly simplifies this process.

The Null conditional operator is syntactic sugar introduced by C# 6.0, which allows developers to first check whether the object is null before accessing its members or elements.nullIf the object isnullthe result of the entire expression isnulland will not throwNullReferenceException。

Basic Syntax

  • ?.- Used to access members (properties, methods, fields)
  • ?[]- Used to access indexers or array elements
Syntax Description
a?.b ifaIf not null, accessa.b; otherwise return null
a?.Method() ifaIf not null, call the method; otherwise, do nothing.
a?[i] ifaIf not null, access the index; otherwise, return null.

Why do we need the Null conditional operator?

Problems with traditional null checks

Before the Null conditional operator was introduced, developers had to write verbosenullCheck code:

Example

// Traditional approach - tedious null checks
string name = null;
if (person != null)
{
    if (person.Address != null)
    {
        if (person.Address.City != null)
        {
            name = person.Address.City.Name;
        }
    }
}

This kind of code is not only verbose, but also error-prone and less readable.

Advantages of the null conditional operator

Using the Null conditional operator, the same logic can be simplified to:

// 使用 Null 条件运算符 - 简洁明了
string name = person?.Address?.City?.Name;

If any step in a chained call is nullnull, the entire expression will returnnull, instead of throwing an exception.


Detailed syntax explanation

1. Member access operator?.

?.The operator is used to safely access the members (properties, methods, fields) of an object.

Access property

Example

class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

class Address
{
    public string City { get; set; }
}

// Safely access properties
Person person = null;
string cityName = person?.Address?.City;  // Returns null, does not throw an exception

Call method

Example

class Calculator
{
    public int Add(int a, int b) => a + b;
}

Calculator calc = null;
int? result = calc?.Add(5, 3);  // Returns null, does not throw an exception

access fields

Example

class DataContainer
{
    public string Value;
}

DataContainer container = null;
string value = container?.Value;  // Returns null, does not throw an exception

2. Element access operator?[]

?[]The operator is used to safely access elements of an array or collection.

Accessing array elements

Example

int[] numbers = null;
int? firstNumber = numbers?[0];  // Returns null, does not throw an exception

numbers = new int[] { 1, 2, 3 };
firstNumber = numbers?[0];  // return 1

Accessing dictionary elements

Example

Dictionary<string, string> dictionary = null;
string value = dictionary?["key"];  // Returns null, does not throw an exception

dictionary = new Dictionary<string, string> { ["key"] = "value" };
value = dictionary?["key"];  // Return "value"

Combining with other operators

Operators Name Purpose Example Return Value
? Nullable type declaration Declaring a nullable value type. int? x = null; null
?? Null-coalescing operator If the left side is null, return the right side. a ?? b b
?. Null conditional operator Safely accessing members a?.b null or the value of b
?[] Null conditional index operator Safely access arrays or collections a?[i] null or the element
?: Conditional (ternary) operator Return different values based on condition a ? b : c b or c

1. With the Null-coalescing operator??Combination

The Null conditional operator is frequently combined with the Null-coalescing operator??Use together to provide a default value:

Person person = null;

// 提供默认值
string cityName = person?.Address?.City ?? "未知城市";
Console.WriteLine(cityName);  // 输出: 未知城市

person = new Person { Address = new Address { City = "北京" } };
cityName = person?.Address?.City ?? "未知城市";
Console.WriteLine(cityName);  // 输出: 北京

Example

using System;

class Address
{
    public string City { get; set; }
}

class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"Hello, I am {Name}");
    }
}

class Program
{
    static void Main()
    {
        Person person = null;

        // Safely access properties
        Console.WriteLine(person?.Name ?? "Unnamed user");

        // Safely access nested properties
        Console.WriteLine(person?.Address?.City ?? "Unknown city");

        // Safely call methods
        person?.SayHello();

        // Safely access arrays
        int[] arr = null;
        Console.WriteLine(arr?[0] ?? -1);
    }
}

Output:

未命名用户
未知城市
-1

2. With the conditional operator?:Combination

Example

Person person = null;
string displayName = person?.Name ?? (person != null ? "Anonymous user" : “User does not exist”);

3. Using in LINQ queries

Example

List<Person> people = new List<Person>
{
    new Person { Name = "Zhang San", Address = new Address { City = "Beijing" } },
    new Person { Name = "Li Si", Address = null },
    null
};

// Safely access properties that may be null
var cities = people
    .Where(p => p?.Address?.City != null)
    .Select(p => p.Address.City)
    .ToList();

Practical application scenarios

Scenario 1: Data binding and UI development

Example

// Safely bind data in WPF or ASP.NET
public string DisplayAddress => $"{Person?.Address?.City ?? "Unknown city"} - {Person?.Address?.Street ?? "Unknown street"}";

// Safely invoke the method
button.Click += (s, e) =>
{
    viewModel?.SaveCommand?.Execute(null);
};

Scenario 2: API response handling

Example

public class ApiResponse<T>
{
    public T Data { get; set; }
    public string Error { get; set; }
}

public class User
{
    public string Name { get; set; }
    public Profile Profile { get; set; }
}

public class Profile
{
    public string AvatarUrl { get; set; }
}

// Safely handle the API response
ApiResponse<User> response = GetUserFromApi();
string avatarUrl = response?.Data?.Profile?.AvatarUrl ?? "default-avatar.png";

Scenario 3: Configuration reading

Example

public class AppConfig
{
    public DatabaseConfig Database { get; set; }
}

public class DatabaseConfig
{
    public string ConnectionString { get; set; }
    public int? Timeout { get; set; }
}

// Safely read configuration
AppConfig config = LoadConfiguration();
string connectionString = config?.Database?.ConnectionString ?? "DefaultConnection";
int timeout = config?.Database?.Timeout ?? 30;

Considerations and best practices

1. Return value types

When using the Null conditional operator, you need to pay attention to the return type of the expression:

Example

Person person = new Person { Name = "Zhang San" };

// For value types, return Nullable<T>
int? nameLength = person?.Name?.Length;  // Type is int?

// For reference types, return the type itself
string name = person?.Name;  // The type is string

2. Avoid overuse

Although the Null conditional operator is convenient, it should not be overused:

Example

// Not recommended - overused, hard to understand
var result = obj1?.Property1?.Method1()?.Property2 ?? defaultValue;

// Recommended - decompose appropriately, improve readability
var temp1 = obj1?.Property1;
var temp2 = temp1?.Method1();
var result = temp2?.Property2 ?? defaultValue;

3. Performance considerations

The Null conditional operator is comparable in performance to explicitnullchecking, but chained calls create temporary variables:

Example

// These two approaches have comparable performance
// Method 1: Traditional check
if (person != null && person.Address != null)
{
    return person.Address.City;
}

// Method 2: Null conditional operator
return person?.Address?.City;

4. Combining with event invocation

The Null conditional operator is particularly suitable for event invocation:

Example

// Traditional way
public event EventHandler SomethingHappened;

protected virtual void OnSomethingHappened()
{
    if (SomethingHappened != null)
    {
        SomethingHappened(this, EventArgs.Empty);
    }
}

// Using the null-conditional operator - more concise
protected virtual void OnSomethingHappened()
{
    SomethingHappened?.Invoke(this, EventArgs.Empty);
}

Practice exercises

Exercise 1: Refactoring code

The following traditionalnullRefactor the checking code to use the Null conditional operator:

Example

// original code
public string GetUserEmail(User user)
{
    if (user != null)
    {
        if (user.Profile != null)
        {
            if (user.Profile.ContactInfo != null)
            {
                return user.Profile.ContactInfo.Email;
            }
        }
    }
    return "No email information";
}

// Your refactored code
public string GetUserEmail(User user)
{
    return user?.Profile?.ContactInfo?.Email ?? "No email information";
}

Exercise 2: Handling collection data

Example

public class Order
{
    public List<OrderItem> Items { get; set; }
    public Customer Customer { get; set; }
}

public class OrderItem
{
    public Product Product { get; set; }
    public int Quantity { get; set; }
}

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class Customer
{
    public string Name { get; set; }
}

// Write amethod,securitygroundGetsubscribe单in theaShang品ofname
public string GetFirstProductName(Order order)
{
    return order?.Items?.FirstOrDefault()?.Product?.Name ?? "No product information";
}

Exercise 3: Comprehensive application

Example

// Create a complete example demonstrating the application of the Null conditional operator in a real project.
public class ShoppingCart
{
    public List<CartItem> Items { get; set; }
    public decimal? CalculateTotal()
    {
        return Items?.Sum(item => item?.Product?.Price * item?.Quantity) ?? 0;
    }
}

public class CartItem
{
    public Product Product { get; set; }
    public int? Quantity { get; set; }
}

C# conditionals

other extensions