Skip to content
Theory · Value and reference types · lesson 7 of 11

The ?., ?? and ??= operators

As you already know, a variable of a class can hold null—a reference that leads nowhere. It is like a message where the space for the document link was left empty: there is nothing to open. Accessing a member through null stops the program with a NullReferenceException.

Here the message and the reference part ways. An empty space in a message is already the sender's mistake, while null in C# is a legitimate value: you can store it, compare it and pass it around. Only accessing a member through it stops the program.

That is why an object that may be missing is checked before use: if (boss != null). Such checks are needed all the time, and C# has three short operators for them.

In this lesson's examples, a type carries a question mark: Person?. It tells the compiler that the variable may end up holding null, and then the compiler does not warn about assigning null to it.

The ?. operator

The ?. operator accesses a member only if the left side is not null:

C#
Person? boss = null;
string? name = boss?.Name;
Console.WriteLine(name == null); // True
boss = new Person();
boss.Name = "Anna";
Console.WriteLine(boss?.Name); // Anna

class Person
{
    public string Name = "";
}

Let's go through the example line by line:

  1. Person? boss = null;—there is no boss yet.
  2. boss?.Name sees null in boss. Nobody touches the field Name, and the whole expression gives null. There is no exception.
  3. The program prints True: name got null.
  4. Once boss holds an object, boss?.Name works like a plain boss.Name and prints Anna.

Read x?.Member like this: if x is null, the result is null; otherwise it is an ordinary member access. Methods are called through ?. too: with null in name, name?.ToUpper() returns null instead of throwing.

The ?? operator

The ?? operator supplies a value for the case of null. The expression a ?? b gives a if it is not null, and b otherwise. Together with ?., it replaces a whole check with if:

C#
Person? guest = null;
Console.WriteLine(guest?.Name ?? "nobody"); // nobody

guest?.Name gives null, and ?? supplies "nobody". If guest held an object, its name would be printed.

Interviewers like to ask how to avoid a NullReferenceException. One answer is to check the reference before using it: with if, ?. or ??.

The ??= operator

The ??= operator assigns only if the variable holds null right now. If there is a value already, the variable does not change:

C#
string? title = null;
title ??= "Untitled";
title ??= "Draft";
Console.WriteLine(title); // Untitled

Let's go line by line:

  1. title holds null, so the first line with ??= puts "Untitled" into it.
  2. The second time, title already holds a string, and "Draft" is not assigned.
  3. The program prints Untitled.

title ??= "Untitled" is a short form of title = title ?? "Untitled". This is how you give a default value to a variable that may still hold null.

Common mistake

A greeting was built from a string and ??, but without parentheses:

C#
string? user = null;
Console.WriteLine("Hello, " + user ?? "guest"); // ✗

The program compiles and prints Hello, with no name. ?? has a lower precedence than +, so C# first runs "Hello, " + user. Adding null to a string gives the string "Hello, ": in string concatenation, null turns into an empty string. The left side of ?? is not null, and "guest" is never used.

You need parentheses: "Hello, " + (user ?? "guest") prints Hello, guest.

Key points

  • x?.Member gives null if x is null, and throws no exception.
  • a ?? b gives a, or b if a is null; a ??= b writes b into a only if a holds null.
  • ?. and ?? often go together: guest?.Name ?? "nobody".
  • ?? has a lower precedence than +, so you write "Hello, " + (user ?? "guest") with parentheses.