十九、可空类型(Nullable)
零、为什么引入可控类型?
| 原因 | 描述 |
|---|---|
| 现实需要 | 业务场景中常有“无值”的可能 |
| 类型安全 | 让值类型支持 null,增强类型系统 |
| 框架友好 | 更好地支持 ORM/序列化/LINQ 等 |
| 表意清晰 | 避免使用特殊默认值,如 -1 或 0 |
| 编译检查 | 编译器能提示未检查 null 的逻辑 |
一、什么是可空值类型?
int? a = null; // 相当于 Nullable<int> a = new Nullable<int>();
-
int?是Nullable<int>的语法糖。 -
可空类型只有两种状态:
-
HasValue = false → 表示为 null
-
HasValue = true → 表示有实际值
-
二、空接合操作符(??)
int? x = null;
int y = x ?? 10; // 若 x 为 null,y 将被赋值为 10
这是 C# 的语法糖,目的是简化 null 检查和默认值设定逻辑。
三、CLR 对 Nullable 的特殊支持
CLR 在处理可空值类型时做了许多优化,确保它们运行时与普通结构体不同:
1️⃣ 装箱行为
int? x = 5;
object o = x; // 只会装箱 int,不会装箱整个 Nullable<int>
-
如果
HasValue = true,只装箱值。 -
如果
HasValue = false,则o被赋为null。
2️⃣ 拆箱行为
object o = 5;
int? x = (int?)o; // 正常
-
如果
o == null,结果为int? = null -
如果
o是值,CLR 会自动包装成Nullable<T>
四、通过 Nullable 调用方法的行为
int? x = 5;
Console.WriteLine(x.GetType()); // 输出:System.Int32
说明:GetType() 是调用的真实值类型的方法,而不是 Nullable 本身。
五、使用建议
| 使用场景 | 推荐方式 |
|---|---|
| 数据库字段对接 | int?, DateTime? |
| 与 UI 控件绑定 | 判断 HasValue 与 Value |
| 避免装箱开销 | 优先使用原始值或 default |
六、示例代码
public static string FormatAge(int? age)
{
return $"年龄:{age?.ToString() ?? "未填写"}";
}
public void Save(int? score)
{
object boxed = score;
Console.WriteLine(boxed ?? "空值");
}
七、可能的坑点 ⚠️
object o = null;
int? i = (int?)o; // ✔️ 正确,返回 null
int i2 = (int)o; // ❌ NullReferenceException
八、面试题推荐:
✅ 1. C# 中值类型能否赋值 null?怎么实现?
默认情况下,值类型(如 int、double、struct)不能赋值为 null,因为它们在内存中直接存储值。
但从 C# 2.0 开始,可以使用 Nullable<T> 或简写形式 T? 来创建可空值类型,从而支持 null:
int? age = null; // 等价于 Nullable<int> age = new Nullable<int>();
Nullable<double> score = 95; // 明确使用 Nullable<T>
✅ 2. Nullable<int> 与 int? 有什么区别?
它们在 语义和运行时表现上完全一致:
-
int?是Nullable<int>的语法糖(语言层面的简写) -
编译后生成的 IL 和 CLR 识别都是
System.Nullable<T>
推荐使用 int?:更简洁、清晰,也符合 C# 风格规范。
✅ 3. 装箱 Nullable<T> 时行为如何?与普通值类型有何不同?
普通值类型装箱
int x = 5;
object o = x; // 生成新的对象,装箱了 int
可空值类型装箱
int? x = 5;
object o = x; // 实际上只装箱 int,不是整个 Nullable<int>
int? y = null;
object o2 = y; // o2 被赋值为 null,而不是 box 任何值
结论:
-
HasValue = true:只装箱内部值 -
HasValue = false:结果就是null(避免无意义装箱)
✅ 4. 如何优雅地给 int? 提供默认值?
使用 空接合操作符 ??,简洁高效:
int? age = null;
int realAge = age ?? 18; // 如果 age 是 null,就用默认值 18
还可以配合表达式简写:
Console.WriteLine($"年龄:{age?.ToString() ?? "未填写"}");
✅ 5. Nullable<T> 能否作为 Dictionary 的 Key?注意事项?
可以,但有几点注意:
✅ 可作为 Key 的前提:
-
Nullable<T>是值类型,可以参与字典键比较 -
Dictionary使用Equals和GetHashCode来判断键的唯一性
⚠️ 注意事项:
-
null本身不能作为 Dictionary 的 Key -
使用
int?作为 Key 时,null和5是不同键:
var dict = new Dictionary<int?, string>();
dict[null] = "空值";
dict[5] = "五";
Console.WriteLine(dict[null]); // 输出:空值
Console.WriteLine(dict[5]); // 输出:五
建议:在使用可空类型作为键时,明确区分是否允许 null 作为 Key,或使用 TryGetValue 安全访问。
作者:世纪末的魔术师
出处:https://www.cnblogs.com/Firepad-magic/
Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号