Перенос кодовой базы на NRT
Применяйте поэтапную стратегию переноса: включайте предупреждения, аннотируйте API, исправляйте проблемы и избегайте ложных срабатываний.
«Перенос кодовой базы на NRT» — бесплатный урок C# Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Сложности миграции
Включение NRT в существующей кодовой базе обычно приводит к появлению сотен предупреждений. Резкий переход рискован. Вместо этого используйте поэтапную миграцию: постепенно включайте предупреждения, исправляйте их файл за файлом и не сбавляйте темп.
Шаг 1: включите только предупреждения
Начните с <Nullable>warnings</Nullable>, а не с enable. Это включает предупреждения, но не считает неаннотированный код ошибочным — безопасное начало миграции.
<!-- Phase 1: warnings only, no breaking change -->
<PropertyGroup>
<Nullable>warnings</Nullable>
</PropertyGroup>
<!-- Phase 2: full enable per file as you migrate -->
<!-- Phase 3: switch to enable globally when done -->Шаг 2: включайте по одному файлу
Добавляйте #nullable enable в начало каждого файла по мере работы с ним. Так изменения ограничиваются файлами, которые Вы редактируете сейчас, а проверка изменений становится управляемой.
#nullable enable
// Now this file has full NRT analysis
public class OrderService
{
private readonly IOrderRepository _repo;
// Compiler now warns about uninitialized non-nullable fields,
// unsafe dereferences, and assignment to non-nullable
public OrderService(IOrderRepository repo) => _repo = repo;
}
// Other files without #nullable enable are still uncheckedКлассификация предупреждений
Предупреждения делятся на две категории: которые безопасно подавить (сущности ORM, поля, внедряемые через DI) и настоящие ошибки (значения, действительно равные null и разыменовываемые в коде). Перед подавлением чего-либо определите, к какой категории относится предупреждение.
// Category 1: safe to suppress with null!
// EF Core navigation properties — set by EF, never null in practice
public class Order
{
public Customer Customer { get; set; } = null!;
}
// Category 2: real bug — must fix
public string GetFullName()
{
return FirstName + " " + LastName; // LastName was string? -- BUG!
}Исправление предупреждения конструктора CS8618
CS8618 возникает, когда свойство, не допускающее null, не задано в конструкторе. Предпочтительное решение — сделать его обязательным параметром конструктора. Используйте = null! только для значений, задаваемых фреймворком.
// BEFORE (CS8618)
public class Product
{
public string Name { get; set; } // warning
public Category Category { get; set; } // warning
}
// AFTER — constructor required:
public class Product
{
public string Name { get; set; }
public Category Category { get; set; }
public Product(string name, Category category)
{
Name = name;
Category = category;
}
}Работа с устаревшими API
Сторонние или устаревшие API могут не иметь аннотаций. Их возвращаемые типы являются неопределёнными — не указывают, допускается ли null. Для явного обозначения присваивайте результаты таких вызовов переменным, допускающим null.
// Legacy API returns 'string' but might be null (oblivious type)
string? legacyResult = OldLibrary.GetValue(); // store as nullable
if (legacyResult is null) return;
// Or convert at the boundary:
string safe = OldLibrary.GetValue() ?? "";
// For third-party types, check if they have NRT annotations:
// NuGet packages often add nullable annotations in newer versionsИспользование #pragma для подавления отдельных предупреждений
Если предупреждение действительно является ложным срабатыванием, а = null! выглядит слишком шумно, используйте #pragma warning disable, ограничив его действие конкретной строкой.
// Suppress for a specific case with explanation:
#pragma warning disable CS8618 // ORM populates this via reflection
public DbSet<Product> Products { get; set; }
#pragma warning restore CS8618
// Or inline with a comment:
public DbSet<Order> Orders { get; set; } = null!; // set by EF CoreПреобразование предупреждений NRT в ошибки
После исправления всех предупреждений в файле добавьте <WarningsAsErrors>Nullable</WarningsAsErrors> (или включите проверку в CI), чтобы предотвратить регрессии: любая новая проблема с nullable-кодом приведёт к сбою сборки.
<!-- After full migration: treat nullable warnings as build errors -->
<PropertyGroup>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Or selectively: -->
<!-- <WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors> -->
</PropertyGroup>Аннотирование общедоступных API
Если Вашу библиотеку используют другие разработчики, аннотации NRT становятся частью контракта её общедоступного API. Возвращайте T?, если результат может быть равен null, и T, если это гарантированно невозможно.
public interface IProductService
{
// Contract: FindById MAY return null, GetById never does
Product? FindById(int id);
Product GetById(int id); // throws if not found
// Collection: never null (may be empty)
IReadOnlyList<Product> GetAll();
// String: may be empty but not null
string GetSummary(int id);
}Метрики и отслеживание миграции
Отслеживайте прогресс, подсчитывая файлы с #nullable enable или выполняя в CI команду dotnet build 2>&1 | grep CS86. Установите целевую дату завершения миграции всего проекта.
# Count NRT warnings in current build
dotnet build 2>&1 | grep -c 'CS860[0-9]\|CS861[0-9]\|CS862[0-9]'
# List files still missing #nullable enable
grep -rL '#nullable enable' src/ --include='*.cs'
# Track in CI: fail if warning count increases
# Set a budget: warnings <= N, where N decreases each sprintБыстрая проверка
Что означает присваивание = null! свойству, не допускающему null?
Повторение: миграция на NRT
Основные выводы:
- Используйте поэтапную миграцию: режим предупреждений → включение для каждого файла → глобальное включение
- Отличайте настоящие ошибки (исправляйте их) от шаблонов ORM/DI (используйте
= null!) - Исправляйте CS8618, требуя значения в конструкторах, а не подавляя предупреждение
- Присваивайте результаты устаревших API переменным типа
T?, чтобы явно обозначить возможность null - Считайте предупреждения о nullable ошибками в CI, чтобы предотвратить регрессии
- Аннотированные общедоступные API становятся понятными контрактами для пользователей
Часто задаваемые вопросы
Урок «Перенос кодовой базы на NRT» бесплатный?
Да — полный текст урока «Перенос кодовой базы на NRT» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Перенос кодовой базы на NRT»?
Применяйте поэтапную стратегию переноса: включайте предупреждения, аннотируйте API, исправляйте проблемы и избегайте ложных срабатываний. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Перенос кодовой базы на NRT»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Включение и понимание NRT
- Аннотации: ?, !, MaybeNull и NotNull
- Операторы условного объединения и объединения с null
- Перенос кодовой базы на NRT