JavaRush /Курсы /C# SELF /Работа с null: операторы ??, ?., ?, !, default

Работа с null: операторы ??, ?., ?, !, default

C# SELF
14 уровень , 3 лекция
Открыта

1. Оператор объединения с null (??)

Ваши знания о nullable-типах не будут полными без умения писать лаконичные и безопасные конструкции. В C# для этого есть два суперполезных оператора: объединение с null (??) и null-условный оператор (?.).

Оператор ?? позволяет задать “значение по умолчанию” для переменной, которая может быть null.

Представьте, что у вас есть имя пользователя, которое может быть null. И если оно действительно null, вы хотите отобразить "Гость". Пример:

string userName = null;
string displayName = userName != null ? userName : "Гость";
Классическая проверка на null

Оператор ?? позволяет записать это компактнее:

string userName = null;
string displayName = userName ?? "Гость";
Использование ?? для значения по умолчанию

Как это работает:

  • Если выражение слева не null, оно возвращается как результат.
  • Если слева null, используется значение справа.

Ещё примеры:

int? age = null;
int displayAge = age ?? -1; // -1 — значение по умолчанию
Console.WriteLine(displayAge); // Выведет -1

string input = null;
string name = input ?? "Гость";
Console.WriteLine($"Привет, {name}!"); // Привет, Гость!

Цепочки операторов

Можно вкладывать ?? в цепочку:

string result = str1 ?? str2 ?? "По умолчанию";
Цепочка операторов ??

Если str1 не null, используется он. Иначе — str2, если и он null — тогда строка "По умолчанию".

2. Null-условный оператор (?.)

Ещё одна частая ситуация — вызвать метод или обратиться к свойству объекта, только если сам объект не null. Пример:

User user = null;
string displayName = user != null ? user.Name : null;
Классическая проверка на null перед обращением к свойству

Оператор ?. позволяет записать это проще:

User user = null;
string displayName = user?.Name;
Null-условный оператор ?.

Если объект user равен null, выражение не вызовет ошибку, а просто вернёт null.

Примеры:

User user = null;

// Без ?. — будет ошибка
// Console.WriteLine(user.Name); // NullReferenceException

Console.WriteLine(user?.Name); // Безопасно — напечатает пустую строку или ничего

Console.WriteLine(user?.GetProfileInfo()); // Аналогично

User[] users = null;
int? count = users?.Length; // null, если users == null

Цепочки операторов

Можно строить целые "цепочки":


string domain = company?.Director?.Email?.Split('@')?[1];
Цепочка ?. для безопасного доступа к вложенным свойствам

Если любая часть пути окажется null, выражение вернёт null, а не выбросит исключение.

Сочетание с ??

Операторы ?. и ?? отлично работают вместе:


string display = user?.Name ?? "Неизвестно";
Безопасный вывод с подстановкой значения по умолчанию

Если user или user.Name равны null, будет выведено "Неизвестно".

3. Что такое ! и зачем он нужен

Оператор подавления предупреждений о null

В современных версиях C# (при включённом NRT) компилятор заботливо предупреждает, если вы обращаетесь к переменной, которая может быть null. Но иногда вы уверены, что всё под контролем. Тогда используется оператор подавления !.


string? possibleNull = GetUserNameMaybeNull();
Console.WriteLine(possibleNull.Length); // Warning: возможен null

Console.WriteLine(possibleNull!.Length); // Компилятор молчит, но если там null — будет исключение

Важно: ! не защищает от ошибок — он просто говорит компилятору "поверь мне". Если переменная действительно равна null, будет NullReferenceException.

Куда не стоит его применять

Не используйте ! вслепую. Хороший стиль — минимизировать его применение. Лучше перестроить код, чтобы null был либо невозможен, либо явно обработан.

4. default — как получить "стандартное" значение типа

Иногда нужно просто "сбросить" переменную в начальное состояние, особенно если не хочется вручную писать 0, false или null.

Для этого существует ключевое слово default.

int a = default;            // a == 0
bool flag = default;        // flag == false
string s = default;         // s == null
double? d = default;        // d == null
Использование default для сброса переменных

В вашем мини-приложении можно, например, сбросить имя или возраст:

userName = default; // null
userAge = default;  // null (если userAge — int?)

5. Различия и нюансы: ?, !, default

Даже опытные разработчики иногда теряются, когда видят ?, ! и default рядом. Разберёмся, кто есть кто.

? — вы разрешаете null

  • Для значимых типов: int? x — теперь x может быть null.
  • Для ссылочных типов: string? s — явно указываете, что переменная может быть null (в режиме NRT).

! — вы утверждаете, что null не будет

  • user!.Name — вы обещаете компилятору, что user точно не null.
  • Работает только в режиме Nullable Reference Types.

default — вы просите значение "по умолчанию"

  • int x = default; — x станет 0
  • string s = default; — s станет null

Сравнение:

Синтаксис Что это? Где работает? При чём тут null?
int?
Nullable значимый тип Везде Можно присвоить null
string?
Nullable ссылочный тип (NRT) В режиме NRT Можно присвоить null
!
Null-forgiving operator В режиме NRT Подавляет предупреждение
default
Значение по умолчанию Везде Для ссылочных — это null

6. Типичные ошибки при использовании ?, ! и default

Ошибка №1: считают, что ! "исцеляет" null.
На самом деле ! просто заставляет компилятор не ругаться. Если значение всё-таки окажется null, программа упадёт с NullReferenceException.

Ошибка №2: забывают проверять .HasValue перед использованием .Value.
Особенно это касается int?, bool? и других nullable-типов. Без такой проверки можно получить исключение InvalidOperationException.

Ошибка №3: используют default там, где нужен "особый" нулевой смысл.
Например, 0 или false могут быть допустимыми значениями, а не сигналами "пустоты". Это может привести к логическим ошибкам.

Ошибка №4: не используют ? для ссылочных типов в режиме NRT — или злоупотребляют им.
Кто-то забывает про ?, и компилятор выдаёт ворох предупреждений. А кто-то, наоборот, везде лепит ?, даже там, где null никогда не бывает. И то, и другое снижает читаемость и надёжность кода.

2
Задача
C# SELF, 14 уровень, 3 лекция
Недоступна
Простой пример с оператором ??
Простой пример с оператором ??
2
Задача
C# SELF, 14 уровень, 3 лекция
Недоступна
Null-условный оператор ?.
Null-условный оператор ?.
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ra Уровень 35 Student
16 декабря 2025
Котлин

fun Product.getPriceInEuros(): Double? = 
   getProductInfo()?.priceInDollars?.let({ convertToEuros(it) })
Тут let вызывает лямбду, у которой на входе прилетает priceInDollars. И результат лямбды можно при желании обрабатывать дальше по цепочке.
Ra Уровень 35 Student
15 декабря 2025
Fun fact: Scope-функции (let, run, with, apply, also) — это уникальная фича Kotlin, которой нет в стандартных C# и Java. Как эмулировать scope-функции в C#: C#: Самописные extension-методы

csharp
public static class ScopeExtensions
{
    // Аналог Kotlin's apply
    public static T Apply<T>(this T obj, Action<T> action)
    {
        action(obj);
        return obj;
    }
    
    // Аналог Kotlin's let
    public static R Let<T, R>(this T obj, Func<T, R> func)
    {
        return func(obj);
    }
}

// Использование:
var person = new Person().Apply(p => 
{
    p.Name = "John";
    p.Age = 25;
});

var nameLength = person?.Let(p => p.Name.Length) ?? 0;
Ra Уровень 35 Student
3 октября 2025
Ничего этого в Java нет, и очень жаль, имхо. Появились конечно @NullMarked и JSpecify, хоть что-то