将代码库迁移到 NRT
采用分阶段迁移策略:启用警告、为 API 添加注解、修复问题,并避免误报。
将代码库迁移到 NRT 是 CoddyKit 上的免费 C# Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 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
当不可为 null 的属性未在构造函数中设置时,就会触发 CS8618。首选的修复方式是在构造函数中要求提供该值。只有对于由框架设置的值,才应使用 = 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 可能没有添加注解。它们的返回类型是未明确标注的(既不是可空类型,也不是不可空类型)。请将其结果赋给可空变量,以明确表达意图。
// 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 强制执行),以防止问题回归——任何新的可空性问题都会导致构建失败。
<!-- 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 契约的一部分。如果结果可能为 null,请返回 T?;如果结果有明确保证,请返回 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?变量,以明确表达意图 - 在 CI 中将可空性警告视为错误,以防止问题回归
- 带注解的公共 API 会为使用者提供清晰的契约
常见问题解答
「将代码库迁移到 NRT」课时是免费的吗?
是的 — 「将代码库迁移到 NRT」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「将代码库迁移到 NRT」这节课中我会学到什么?
采用分阶段迁移策略:启用警告、为 API 添加注解、修复问题,并避免误报。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「将代码库迁移到 NRT」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。