0Pricing
C# Academy · Урок

Включение и понимание NRT

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

«Включение и понимание NRT» — бесплатный урок C# Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Ошибка на миллиард долларов

Тони Хоар (изобретатель null) назвал это своей «ошибкой на миллиард долларов». В C# любой ссылочный тип по умолчанию мог иметь значение null, из-за чего NullReferenceException стал самой частой ошибкой во время выполнения. Nullable Reference Types (NRT) исправляют эту проблему на уровне компилятора.

Включение контекста допустимости null

Включите NRT глобально в файле проекта или для отдельного файла с помощью директив. После включения компилятор считает ссылочные типы по умолчанию не допускающими значение null.

<!-- In .csproj — enable for the whole project -->
<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

// Or per-file:
#nullable enable
// ... code with NRT warnings
#nullable disable
// ... code without NRT warnings

Ссылочные типы, допускающие и не допускающие null

При включённых NRT: string означает, что значение никогда не равно null, а string? — что оно может быть равно null. Компилятор предупреждает о присваивании null типу, не допускающему null, или о разыменовании типа, допускающего null, без проверки.

#nullable enable

string nonNull = "Hello";    // OK
string? maybeNull = null;    // OK — explicitly nullable

nonNull = null;              // WARNING: CS8600
Console.WriteLine(maybeNull.Length); // WARNING: CS8602 — may be null

// Fix:
if (maybeNull is not null)
    Console.WriteLine(maybeNull.Length); // safe

Анализ потока

Компилятор выполняет анализ потока — отслеживает состояние null в условных выражениях и ветвлениях и подавляет предупреждения, когда может доказать, что значение не равно null.

string? name = GetName();

// After null check, name is treated as non-null:
if (name is not null)
    Console.WriteLine(name.Length);  // no warning

// Same with early return:
if (name is null) return;
Console.WriteLine(name.Length);     // no warning — null was returned

// Pattern matching:
if (name is string s)
    Console.WriteLine(s.ToUpper()); // no warning

Предупреждения о допустимости null: CS8600–CS8629

Важно понимать следующие предупреждения NRT: CS8600 (присваивание null типу, не допускающему null), CS8602 (разыменование значения, которое может быть равно null), CS8603 (возврат null для типа, не допускающего null), CS8618 (неинициализированное поле, не допускающее null).

#nullable enable

public class Order
{
    public string CustomerName { get; set; } // CS8618: not initialized
    public string? TrackingNumber { get; set; } // OK — nullable

    public Order(string name)
    {
        CustomerName = name; // now initialized — CS8618 gone
    }
}

string? s = GetValue();
Console.WriteLine(s.Length); // CS8602: s might be null

Инициализация полей, не допускающих null

CS8618 возникает, если поле или свойство, не допускающее null, не задано в конструкторе. Возможные решения: инициализировать его в объявлении, сделать обязательным через конструктор или использовать оператор подавления null для шаблонов DI/ORM.

// Option 1: Initialize in declaration
public string Name { get; set; } = "";

// Option 2: Require via constructor
public class Product
{
    public string Name { get; }
    public Product(string name) => Name = name;
}

// Option 3: null-forgiving for ORM entities
public string Name { get; set; } = null!;
// null! tells compiler "trust me, this will be set by EF Core"

Допустимость null в обобщённых типах

Для параметров обобщённых типов можно задать ограничение, запрещающее null. Ограничение where T : notnull гарантирует, что T никогда не будет равно null.

// Without constraint: T could be nullable
public T? Find<T>(int id) { ... }

// With notnull: T must be a non-nullable type
public T FindRequired<T>(int id) where T : notnull
{
    var result = InternalFind<T>(id);
    return result ?? throw new KeyNotFoundException();
}

// T? is meaningful only when T is known to be non-nullable
public T? FindOrDefault<T>(int id) where T : class { ... }

Допустимость null в интерфейсах и переопределениях

При реализации интерфейса или переопределении метода аннотации допустимости null должны совпадать. Компилятор проверяет, что реализации допускают null не в большей степени, чем это объявлено в интерфейсе.

public interface IRepository<T>
{
    T? FindById(int id);       // may return null
    T GetOrThrow(int id);      // never null
}

// Implementation:
public class ProductRepo : IRepository<Product>
{
    public Product? FindById(int id)  => _db.Find(id);
    public Product  GetOrThrow(int id) =>
        _db.Find(id) ?? throw new KeyNotFoundException();
}

Типы значений, допускающие null, и ссылочные типы, допускающие null

Не путайте int? (Nullable<int> — оболочка типа значения, существовавшая ещё до C# 8) с string? (аннотация NRT, действующая только во время компиляции и не создающая накладных расходов во время выполнения).

// Nullable value type (runtime Nullable<int>)
int? age = null;
bool hasValue = age.HasValue;
int value  = age.GetValueOrDefault();

// Nullable reference type (compile-time annotation only)
string? name = null;
// string? has NO runtime wrapper — it's just a compile-time hint
// null check is still needed at runtime
if (name is not null)
    Console.WriteLine(name.ToUpper());

Подавление предупреждений с помощью !

Оператор подавления null (!) подавляет конкретное предупреждение о допустимости null, когда Вы располагаете более точной информацией, чем компилятор. Используйте его умеренно и документируйте причину безопасности такого использования.

// Use ! when you know the value cannot be null
var user = _db.Users.FirstOrDefault(u => u.Email == email);
var name = user!.Name; // user is guaranteed by business logic

// EF Core navigation properties set by the framework:
public class Order
{
    public Customer Customer { get; set; } = null!; // set by EF Core
}

Практический пример: ответ API с допускаемыми значениями null

Обёртка API, в которой поля могут присутствовать или отсутствовать в ответе JSON, — аннотации допустимости null ясно передают намерение разработчика.

#nullable enable

public class WeatherResponse
{
    public string City { get; set; } = "";     // always present
    public double Temperature { get; set; }     // always present
    public string? Description { get; set; }    // optional field
    public string? AlertMessage { get; set; }   // only when there's an alert
}

// Consumer:
var weather = await api.GetWeatherAsync("London");
Console.WriteLine(weather.City);            // safe
Console.WriteLine(weather.Description?.ToUpper() ?? "N/A"); // safe null-handling

Быстрая проверка

Каковы накладные расходы во время выполнения у Nullable Reference Types (string?) по сравнению с обычными ссылочными типами (string)?

Итоги: включение и понимание NRT

Основные выводы:

  • Включайте глобально с помощью <Nullable>enable</Nullable> в .csproj
  • string = никогда не равно null; string? = может быть равно null
  • Анализ потока отслеживает состояние null в ветвлениях и возвращаемых значениях
  • Аннотации NRT действуют только во время компиляции и не создают накладных расходов во время выполнения
  • CS8618: инициализируйте поля, не допускающие null, в конструкторе или используйте = null! для ORM
  • Используйте ! умеренно — документируйте причину безопасности оператора подавления null

Часто задаваемые вопросы

Урок «Включение и понимание NRT» бесплатный?

Да — полный текст урока «Включение и понимание NRT» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Включение и понимание NRT»?

Включайте контекст допуска null, разбирайтесь в ссылочных типах, допускающих и не допускающих null, и читайте предупреждения компилятора. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Включение и понимание NRT»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Включение и понимание NRT
  2. Аннотации: ?, !, MaybeNull и NotNull
  3. Операторы условного объединения и объединения с null
  4. Перенос кодовой базы на NRT
← Назад к C# Academy