Что означают два вопросительных знака подряд в C#?
Оператор объединения с null (??) в C#
Два вопросительных знака ?? в C# — это null-coalescing operator (оператор объединения с null). Он используется для того, чтобы проверить значение на null и, если оно null, подставить другое значение. Это очень удобная замена длинным конструкциям с if.
Давайте разберём ваш первый пример:
string name = userName ?? "Гость";
Эта строка работает так:
- Проверяется переменная
userName. - Если
userNameНЕ равенnull, то вnameзаписывается значениеuserName. - Если
userNameравенnull, то вnameзаписывается строка"Гость".
Без этого оператора вам пришлось бы писать так:
string name;
if (userName != null)
{
name = userName;
}
else
{
name = "Гость";
}
Согласитесь, с ?? гораздо короче и понятнее.
Оператор присваивания объединения с null (??=)
Ваш второй пример с ??= — это более новая фича (появилась в C# 8.0). Это null-coalescing assignment operator. Он делает то же самое, но сразу присваивает значение переменной, если она равна null.
list ??= new List<string>();
Эта строка означает:
- Проверяем переменную
list. - Если
listравенnull, то создаём новый списокnew List<string>()и присваиваем егоlist. - Если
listуже неnull, ничего не делаем.
Без ??= вам пришлось бы писать:
if (list == null)
{
list = new List<string>();
}
Важные моменты
- Типы должны совпадать или быть совместимыми. Например, если слева
int?(nullable int), то справа можно поставить обычныйint, но не строку. - Оператор можно использовать цепочкой. Например:
result = a ?? b ?? c ?? "default";— будет взято первое не-null значение. - Не путайте с тернарным оператором
? :. Тернарный оператор проверяет любое булево условие, а??проверяет только наnull.
Ещё примеры для закрепления
// Пример с числами
int? count = null;
int actualCount = count ?? 0; // actualCount = 0
// Пример с объектами
Customer customer = GetCustomerFromDatabase(id);
string displayName = customer?.Name ?? "Неизвестный клиент"; // ?. — это null-conditional operator
// Пример с ??=
List<int> numbers = null;
numbers ??= new List<int>(); // numbers теперь пустой список
numbers.Add(42); // Работает, не будет NullReferenceException
Используйте эти операторы, чтобы сделать код чище и избежать ошибок NullReferenceException. Это один из основных паттернов современного C#.
Альтернативный взгляд: когда НЕ стоит использовать ??
Хотя оператор ?? очень удобен, у него есть один нюанс: он всегда вычисляет правую часть, даже если левая часть не равна null. В большинстве случаев это не проблема, но если справа стоит дорогостоящая операция (например, запрос к базе данных), это может ударить по производительности.
Пример плохого использования:
string data = cache.Get("key") ?? FetchDataFromDatabase(); // FetchDataFromDatabase() выполнится ВСЕГДА!
В этом случае FetchDataFromDatabase() будет вызван независимо от того, есть ли данные в кеше. Чтобы избежать этого, используйте старый добрый if:
string data = cache.Get("key");
if (data == null)
{
data = FetchDataFromDatabase();
}
Или, если вы используете C# 9.0+, можно применить паттерн-матчинг с коротким замыканием:
string data = cache.Get("key") ?? (data = FetchDataFromDatabase()); // Всё равно выполнится, но не так очевидно
Вывод: ?? отлично подходит для простых значений (констант, простых выражений), но для тяжелых вызовов лучше использовать явную проверку на null с помощью if.