Nullable reference
Тема дорожной карты · .NET / C# Developer
Nullable reference types (NRT) — фича C# 8, которая делает обработку null частью системы типов. До NRT любая ссылочная переменная могла быть null, и компилятор не предупреждал. С NRT тип string обозначает «гарантированно не null», а string? — «может быть null». Компилятор статически анализирует код и выдаёт предупреждения, когда null-значение попадает туда, где не ожидается. По сути — статический null-safety на уровне типов, аналог Option<T> из ML-языков, только встроенный в существующий тип.
Как это работает
Включается через <Nullable>enable</Nullable> в .csproj или #nullable enable в файле. Компилятор отслеживает «flow analysis» — на каждой инструкции вычисляет, может ли переменная быть null, и сравнивает с типом. Атрибуты разметки ([NotNull], [MaybeNull], [NotNullWhen(true)], [MemberNotNull]) позволяют выразить контракты, которые компилятор сам не вывел бы. Операторы ! (null-forgiving) подавляют предупреждение в местах, где разработчик уверен. Default-значения для типа T? — default(T) для value types и null для reference types.
Когда применять
Включайте NRT во всех новых проектах с первого дня — стоимость нулевая после привыкания, а инциденты с NullReferenceException практически исчезают. Для legacy-проектов выбирайте миграцию по файлам: #nullable enable в одном файле за раз, исправляйте предупреждения, двигайтесь дальше. Не отключайте NRT через ! массово — это превращает фичу в обвес, не дающий пользы. Лучше явно проверить null или указать в типе.
Типичные ошибки
Массовое подавление через ! — теряется вся ценность фичи. Игнорирование [NotNullWhen] атрибутов в string.IsNullOrEmpty и Try* методах — компилятор не выведет null-safety, если контракт не выражен. Использование string.Empty вместо null для «отсутствующего значения» — теряется семантическая разница между «пустой строкой» и «значение не передано». Включение NRT без обновления интерфейсов API — клиенты внезапно получают предупреждения на каждом обращении. Игнорирование default! в generic-классах для полей — компилятор сообщит «non-nullable field is uninitialized» на каждом конструкторе.