可空值类型用“?”声明(如int?),本质是Nullable,需用HasValue或== null判断再操作,??和?.用于安全处理null,参与运算时结果仍为可空类型,数据库和JSON序列化中需注意null默认行为差异。

可空值类型怎么声明和初始化
值类型默认不能为 null,要让它能存 null,必须显式用 ? 语法声明为可空类型。本质是 Nullable<t></t> 的简写,比如 int? 等价于 Nullable<int></int>。
常见错误:直接对未初始化的可空变量做运算,比如 int? x; Console.WriteLine(x + 1);——这会抛出 InvalidOperationException: Nullable object must have a value.
-
int? a = null;是安全的初始化方式 -
int? b = 42;或int? c = new int?(42);效果相同 - 不能写
int? d = null + 1;,编译不过;也不能对null值解引用(如d.Value)而不先检查 - 推荐用
HasValue判断是否含有效值,或更简洁地用== null(int?重载了相等运算符)
用 ?? 和 ?. 处理 null 值最常用场景
?? 是空合并运算符,只在左操作数为 null 时返回右操作数;?. 是空条件成员访问,遇到 null 直接短路返回 null(不抛异常)。
典型误用:把 ?. 用在值类型上却忘了结果也是可空的。例如 DateTime? d = obj?.CreatedTime;,即使 obj.CreatedTime 是 DateTime,?. 的结果仍是 DateTime?。
-
string name = person?.Name ?? "Unknown";—— 安全取名,无person就用默认值 -
int? count = items?.Count();——items为null时不会调用Count(),返回null -
int safeCount = items?.Count() ?? 0;—— 把可能的null转成确定的0 - 链式调用如
user?.Address?.City?.Length返回的是int?,不是int,后续不能直接参与数值计算
可空类型参与运算和比较要注意什么
可空类型之间、或与非可空类型混合运算时,C# 会自动提升(lift)运算符,但结果仍是可空的。比较行为也和直觉略有差异。
容易踩坑:用 == 比较两个 int?,其中一个是 null,结果是 false(不是异常),但 null == null 是 true;而 if (x > 0) 中若 x 是 null,整个条件为 false,不会报错也不会进分支。
-
int? a = 5, b = null; var sum = a + b;→sum是null,不是异常 -
bool result = (a == b);→ 当a和b都为null时为true,否则为false -
if (x != null && x.Value > 10)写法啰嗦,推荐if (x > 10)(C# 自动处理) - 需要明确区分“未赋值”和“零值”时,别依赖隐式转换,老实用
HasValue+Value
数据库字段映射和 JSON 序列化中的 null 行为
ORM(如 EF Core)和 JSON 库(如 System.Text.Json)默认把数据库允许 NULL 的整型/日期字段映射为对应可空类型,这是合理且必要的。但序列化时容易忽略默认行为差异。
关键点:System.Text.Json 默认**不序列化值为 null 的可空属性**(除非显式配置 DefaultIgnoreCondition = JsonIgnoreCondition.Never),而 Newtonsoft.Json 默认会输出 "Prop": null。
- EF Core 中,
[Required]不能加在int?上——它本就表示可选;要用[Required]应该配int+ 数据库设为 NOT NULL - API 返回 DTO 里有
DateTime?字段,前端收到null是正常情况,别当成 bug - 反序列化时,JSON 中
"age": null能正确绑定到int?,但"age": "abc"会抛JsonException - 测试边界时,一定要覆盖
null输入路径,尤其涉及计算、格式化或数据库插入的逻辑
可空类型的真正复杂点不在语法,而在业务语义:同一个 null,在数据库里代表“未填写”,在 API 响应里可能代表“暂不可用”,在缓存中又可能表示“查询过但无结果”。这些含义不会被编译器识别,得靠命名、注释和上下文来守住。


















