可空引用类型是编译期静态分析机制,string? 仅为编译器标记而非新类型;int? 是运行时 Nullable<T> 结构体,二者机制不同。

可空引用类型不是“让引用类型能为 null”,而是编译器在你写 string s 时,默认认为它「不该为 null」;而 string? s 才是明确告诉编译器「允许为 null」——这是纯编译期检查,不改变运行时行为,也不生成新类型。
为什么 string? 不会崩溃,但 int? 会抛 InvalidOperationException
因为二者根本不是同一类机制:int? 是 Nullable<int></int> 结构体,有真实的 HasValue 和 Value 字段,访问 .Value 时若未赋值就抛异常;而 string? 只是编译器加的一层静态分析标记,s.Length 在运行时仍可能触发 NullReferenceException,但编译器会在你没检查 s != null 就调用 .Length 时提前警告。
-
int?的 null 安全靠运行时判断(HasValue)或语法糖(??、GetValueOrDefault) -
string?的 null 安全靠编译器警告(如 CS8602:“Dereference of a possibly null reference”) - 开启
#nullable enable后,string s = null;会直接报 CS8625,而string? s = null;合法
#nullable enable 必须在项目或文件级显式启用
它不是默认打开的。即使你用了 string?,若没启用该功能,编译器完全忽略问号,也不报任何警告。
- 项目级启用:在
.csproj中添加<Nullable>enable</Nullable> - 文件级启用:在 .cs 文件顶部加
#nullable enable(适合渐进迁移) - 别误用
#nullable restore或#nullable disable包裹整段逻辑——这会关闭检查,等于白启 - EF Core 模型中写
public string? Name { get; set; },只有启用后才能捕获Name.Length前未判空的隐患
和数据库字段映射、JSON 反序列化的关系很弱
可空引用类型不影响运行时数据流。数据库里 VARCHAR NULL 列映射成 string 还是 string?,取决于你手动怎么写 POCO;System.Text.Json 反序列化时,"name": null 总是能进 string? 字段,也能进 string 字段(此时值为 null),但后者在启用可空检查后会立刻触发 CS8603(“Possible null reference assignment”)。
-
string Name+"name": null→ 运行时Name == null,但编译器警告你“不该赋 null” -
string? Name+"name": null→ 合法,无警告 - 别指望它自动修复 EF Core 的
IsRequired(false)配置——那是模型约束,和可空性注解无关 - JSON 中传
"name": 123会反序列化失败,这不是可空性问题,而是类型不匹配(JsonException)
最容易被忽略的点:可空性状态会“传染”,且不能靠强制转换绕过
一旦某个变量被推断为 maybe-null,所有基于它派生的表达式(比如 s?.Trim() 的结果、LINQ 中的 select s)也会带 maybe-null 状态。你不能用 (string)s 强制转成 not-null 来消警告——编译器仍认为它可能为 null,除非你显式用 ! 断言(s!.Length)或用 ?? 提供 fallback。


















