Операторы ?., ?? и ??=
Вы уже знаете, что переменная класса может хранить null — ссылку, которая никуда не ведёт. Это как письмо, где вместо ссылки на документ осталось пустое место: открыть нечего. Обращение к члену через null останавливает программу исключением NullReferenceException.
Здесь письмо и ссылка расходятся. Пустое место в письме — уже ошибка отправителя, а null в C# — законное значение: его можно хранить, сравнивать и передавать. Останавливает программу только обращение к члену через него.
Поэтому объект, которого может не оказаться, проверяют перед обращением: if (boss != null). Такие проверки нужны постоянно, и для них в C# есть три коротких оператора.
В примерах этого урока у типа стоит знак вопроса: Person?. Он сообщает компилятору, что в переменной может оказаться null, и тот не предупреждает о присваивании null.
Оператор ?.
Оператор ?. обращается к члену, только если слева не null:
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 = "";
}Давайте разберём пример по строкам:
Person? boss = null;— начальника пока нет.boss?.Nameвидит вbossзначениеnull. К полюNameникто не обращается, и всё выражение даётnull. Исключения нет.- Печатается
True: вnameпопалnull. - Когда в
bossпоявляется объект,boss?.Nameработает как обычноеboss.Nameи печатаетAnna.
Выражение x?.Член читается так: если x равен null, результат — null, иначе — обычное обращение к члену. Через ?. вызывают и методы: name?.ToUpper() при null в name вернёт null вместо исключения.
Оператор ??
Оператор ?? подставляет значение на случай null. Выражение a ?? b даёт a, если a не null, а иначе — b. Вместе с ?. он заменяет целую проверку через if:
Person? guest = null;
Console.WriteLine(guest?.Name ?? "nobody"); // nobodyguest?.Name даёт null, и ?? подставляет "nobody". Если бы в guest был объект, напечаталось бы его имя.
На собеседовании часто спрашивают, как избежать NullReferenceException. Один из ответов — проверять ссылку перед обращением: через if, ?. или ??.
Оператор ??=
Оператор ??= присваивает, только если в переменной сейчас null. Если значение уже есть, переменная не меняется:
string? title = null;
title ??= "Untitled";
title ??= "Draft";
Console.WriteLine(title); // UntitledРазберём по строкам:
- В
titleлежитnull, поэтому первая строка с??=кладёт в него"Untitled". - Во второй раз в
titleуже есть строка, и"Draft"не присваивается. - Печатается
Untitled.
title ??= "Untitled" — короткая форма для title = title ?? "Untitled". Так задают значение по умолчанию переменной, в которой мог остаться null.
Частая ошибка
Приветствие собрали из строки и ??, но без скобок:
string? user = null;
Console.WriteLine("Hello, " + user ?? "guest"); // ✗Программа компилируется и печатает Hello, без имени. У ?? приоритет ниже, чем у +, поэтому C# сначала выполняет "Hello, " + user. Сложение строки с null даёт строку "Hello, ": null при сложении превращается в пустую строку. Слева от ?? оказывается не null, и "guest" не подставляется.
Нужны скобки: "Hello, " + (user ?? "guest") печатает Hello, guest.
Главное
x?.Члендаётnull, еслиxравенnull, и не выбрасывает исключение.a ?? bдаётa, а если тамnull—b;a ??= bзаписываетbвa, только если вaлежитnull.?.и??часто пишут вместе:guest?.Name ?? "nobody".- У
??приоритет ниже, чем у+, поэтому"Hello, " + (user ?? "guest")пишут со скобками.