JavaRush /Курсы /C# SELF /Введение в Nullable Reference Types

Введение в Nullable Reference Types

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

1. Проблема NullReferenceException

Практически каждый начинающий (и не только) программист на C# хотя бы раз сталкивался с этим пугающим сообщением:


System.NullReferenceException: Object reference not set to an instance of an object.

Вот вам классика жанра:

string hello = null;
Console.WriteLine(hello.Length); // БАМ! NullReferenceException

Смысл прост: вы пытаетесь обратиться к объекту, которого не существует. То есть переменная ссылается на null — пустое место вместо объекта.

Почему ошибку легко допустить?

Потому что большинство ссылочных типов в C# исторически могли принимать значение null. Программисты часто забывали проверить, что переменная действительно указывает на объект, и, как следствие, ловили знаменитый NullReferenceException.

Если бы программист получал доллар за каждый NullReferenceException, он бы давно написал свою операционную систему.

Почему это проблема?

Ранее мы научились делать так, чтобы значения типа int? или double? могли быть равны null — для случаев, когда нам нужно явно обозначить "отсутствие значения" (например, поле в базе может быть не заполнено).

Однако ссылочные типы (например, string, любые классы, массивы и т.д.) всегда могли быть равны null. Так было с самого рождения C#. Это удобно, но и опасно — потому что язык не заставлял нас явно думать: "А может ли эта ссылка быть пустой?"

2. Эволюция: Nullable Reference Types (NRT)

5 лет назад было представлено нововведение, которое перевернуло с ног на голову подход к борьбе с NullReferenceExceptionNullable Reference Types (NRT).

string s = "hello";   // not-nullable reference
string? maybe = null; // nullable reference
Различие между not-nullable и nullable ссылочными типами

Главная идея:

  • Строго отделить переменные-ссылки, которые точно не могут быть равны null, от тех ссылок, которые могут быть пустыми.
  • Начать помогать программисту видеть потенциальные проблемы с null прямо на этапе компиляции, а не в рантайме (когда уже поздно).

В новых версиях C# изменился подход к объявлениям переменных-ссылок: по умолчанию нельзя присваивать null, если только вы явно не разрешили это.

Вот так просто — всего один символ ?, а смысл переменной сразу меняется!

3. Включение Nullable Reference Types

По умолчанию такой строгий синтаксис отключен в большинстве проектов, чтобы не ломать старый код. Но современные шаблоны проектов в Visual Studio, Rider и .NET CLI уже создают проекты с включёнными NRT — или, по крайней мере, настоятельно рекомендуют это сделать.

Чтобы точно узнать, включены ли NRT в вашем проекте, найдите в файле .csproj строчку:

<Nullable>enable</Nullable>
Включение NRT в проекте

Если этой строки нет, можно добавить её вручную — это безопасно.

Как это влияет на код?

  • Если NRT выключены (старое поведение): string s = null; — никаких ошибок, всем всё равно.
  • Если включены: компилятор начнёт ругаться, если вы пытаетесь засунуть null в то, что не должно быть null.

4. Примеры с NRT

Простой пример


#nullable enable // Эта строка включает проверку NRT для данного файла

string notNullable = "Привет";
string notNullable2 = null; // ОШИБКА компиляции!
string? nullableString = null; // Всё нормально, мы явно разрешили null
Пример строгой проверки на null

В первой строке мы объявили строку, которая всегда должна быть ссылающейся на реальный объект.
В третьей — объявили строку, которая может быть null.

Проверка на null

void PrintLength(string? s)
{
    // Компилятор будет ругаться: "А что, если s == null?"
    Console.WriteLine(s.Length); 
    
    // А так - хорошо
    if (s != null)
    {
        Console.WriteLine(s.Length); 
    }
}

Компилятор теперь помогает не забыть о проверках!

Предупреждения компилятора

Если мы проигнорируем предупреждение и всё равно попытаемся обратиться к nullable-переменной без проверки — получим новое (очень полезное!) предупреждение компилятора. Это не ошибка (программа всё ещё соберётся), но жёлтая "лампочка" намекает: "Ты уверен, друг?"

Сравниваем режимы работы:

C#-тип Может быть null? Legacy-режим (до NRT) NRT-режим (#nullable enable)
int Нет Нет Нет
int? Да Да Да
string Да Нет (всегда можно) Нет (по умолчанию нельзя)
string? Да Нет Да

5. Советы: как и зачем использовать NRT

  • Снижение количества багов: Меньше неожиданных падений из-за null, больше радости для разработчика и пользователя.
  • Ясность кода: Сразу видно, что может быть пустым, а что — всегда должно быть заполнено.
  • Помощь от компилятора: Пора признать — он действительно старается для нас! Предупреждения NRT — ценный источник информации о потенциальных ошибках.

Где это незаменимо

  • В больших проектах, где много людей работают с одним кодом.
  • В API и публичных библиотеках — чтобы объяснить другим, что им можно и нельзя делать.
  • Там, где важна надёжность (например, банковские приложения, медицинские системы и др.)

6. Типичные ошибки и подводные камни

  • "Забыл поставить ?"
    Присваиваешь null обычной строке (string s = null;) — а компилятор ругается, потому что по умолчанию теперь обычная строка не должна быть null.
  • "Переусердствовал с ?"
    Делаешь все переменные string?, лишь бы компилятор не ругался. Смысл подхода в том, чтобы тщательно размечать, где именно допускается пустое значение.
  • "Неправильно понял предупреждение"
    Игнорируешь предупреждение, а потом ловишь NullReferenceException там, где рассчитывал на заботу компилятора.
2
Задача
C# SELF, 14 уровень, 1 лекция
Недоступна
Объявление Nullable Reference Types
Объявление Nullable Reference Types
2
Задача
C# SELF, 14 уровень, 1 лекция
Недоступна
Проверка на Null
Проверка на Null
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Александр Уровень 24
8 ноября 2025
со строками ок, а с интами то что по итогу? они вместо стека в кучу пойдут?
Yurii N Уровень 51
4 января 2026
Nullable<T> — это struct-обёртка, которая хранится как обычный value type. struct Nullable<T> where T : struct { public bool HasValue; public T Value; }