判断对象是否为 null 用 == null 就够了,C# 中引用类型默认支持该写法,简洁安全;可空值类型同样适用;字符串应优先用 string.IsNullOrEmpty;启用 nullable 引用类型并配合 ArgumentNullException.ThrowIfNull 等手段可系统性防控空引用异常。

判断对象是否为 null 用 == null 就够了
绝大多数情况下,null 检查只需要一行: if (obj == null)。C# 的 == 运算符对引用类型默认就是引用相等性比较,和 ReferenceEquals(obj, null) 行为一致,且更简洁、可读性更好。
别用 obj.Equals(null) —— 这会直接抛出 NullReferenceException;也别绕路写 object.ReferenceEquals(obj, null),除非你在写底层框架或需要明确语义的极端场景。
- 值类型(如
int、DateTime)不能为null,但可空值类型(int?、DateTime?)可以用== null安全判断 - 字符串是引用类型,
string.IsNullOrEmpty(s)是语义检查,不是 null 判定替代方案;它内部第一行就是s == null - 自定义类型重载了
==运算符?只要没改 null 比较逻辑(极少见),obj == null依然安全
开启 nullable 引用类型后,编译器能提前拦住大部分空引用
这是 C# 8.0 起最有效的预防手段。启用后,编译器会把未标注可空性的引用类型(如 string、MyClass)当作「不应为 null」来静态分析,并在可能赋 null 或解引用前报警告(CS8602、CS8600 等)。
关键不是“加了就万事大吉”,而是要配合项目配置和渐进式迁移:
- 在
.csproj中添加enable - 已有代码会大量报黄线——先用
!后缀断言(如obj!.ToString()),但这是临时止血,不是修复 - 真正修复是补上
?标注(如把参数改成string? name),再补运行时检查 - 第三方库若没标注 nullable,编译器会按“宽松模式”处理,此时仍需手动防护
避免在属性 getter 里隐式触发 NullReferenceException
常见陷阱:把空检查写在 setter,却在 getter 里直接访问子成员。比如 public string Name => _person.Name;,而 _person 是 null —— 这个 getter 一调就崩,且堆栈不提示源头。
正确做法是让 getter 自包含防御逻辑,尤其当字段/属性本身可能未初始化时:
- 用空合并运算符:
public string Name => _person?.Name ?? string.Empty; - 或显式判空:
public string Name => _person is null ? string.Empty : _person.Name; - 如果业务上
_person必须非空,那就该在构造或 setter 里强制校验,而不是把风险甩给 getter - 不要依赖 IDE 自动生成的属性模板(如
prop),它们默认不带空安全逻辑
用 ArgumentNullException.ThrowIfNull() 替代手写 if-throw
.NET 6+ 提供了标准、高效的空参数校验方式:ArgumentNullException.ThrowIfNull(obj, nameof(obj))。它比传统写法更轻量(无异常对象分配开销)、语义更清晰,且被 JIT 优化过。
注意它只适用于 public 方法的参数校验场景:
- 别在 private 方法或内部逻辑里滥用——那里更适合用
Debug.Assert或直接if (obj == null) throw ... - 旧项目升级时,逐个替换
if (obj == null) throw new ArgumentNullException(...)即可,行为完全一致 - 如果目标框架低于 .NET 6,可用
Guard.Against.Null(obj, nameof(obj))(如使用 Microsoft.Toolkit.Extensions),但优先升级框架 - 这个方法不解决“对象内部字段为空”的问题,只管入参本身
空引用的本质从来不是语法问题,而是控制流没覆盖到的分支、契约没声明清楚的接口、或者初始化顺序被忽略的时机。编译器提示、统一校验入口、getter 防御意识——这三块漏掉任何一块,都可能让 NullReferenceException 在深夜三点弹出来提醒你。











