markdown
# 在 C# 编程(尤其是业务开发)中,空值校验是保障程序稳定性、避免空引用异常(`NullReferenceException`)的核心手段。结合你这段物料分类新增的代码,我会从**场景+原因+示例**三个维度,清晰告诉你什么情况必须做空值校验。
### 一、必须做空值校验的核心场景
#### 1. 外部输入/入参校验(最基础也最关键)
**适用场景**:
- 方法/函数的入参(尤其是外部系统、用户输入、第三方接口传递的参数)
- 从配置文件、数据库、消息队列读取的数据
- API 接口接收的请求参数
**为什么要做**:
外部数据不受你的代码控制,可能是 null、空字符串、格式错误等,直接使用会导致程序崩溃。
**对应你代码的示例**:
```csharp
// 你的代码中对入口参数的校验(必须做)
if (receiveEventInfo == null || string.IsNullOrWhiteSpace(receiveEventInfo.Content))
{
LogTextHelper.Warn("物料分类新增失败:接收的事件信息为空或内容为空");
return;
}
如果不校验 :receiveEventInfo.Content 为 null 时,会直接抛出 NullReferenceException,导致整个方法崩溃,甚至影响调用方。
2. 反序列化/类型转换后的数据校验
适用场景:
- JSON/XML 反序列化后的对象(如你代码中
JsonConvert.DeserializeObject的结果) - 数据库查询结果(如
FindSingle查不到数据会返回 null) - 类型转换(如
Convert.ToInt32、TryParse失败的情况)
为什么要做 : 反序列化/查询可能因数据格式错误、数据不存在等原因返回 null,后续访问其属性(如 eventInfo.Archive)会直接报错。
对应你代码的示例:
csharp
// 反序列化后的对象校验(必须做)
if (eventInfo == null || string.IsNullOrWhiteSpace(eventInfo.Archive))
{
LogTextHelper.Warn("物料分类新增失败:反序列化后的Archive数据为空");
return;
}
如果不校验 :eventInfo 为 null 时,eventInfo.Archive 会触发空引用异常,中断业务流程。
3. 嵌套对象/多级属性访问前
适用场景:
- 访问对象的嵌套属性(如
dTOArchive.Name.ZhCN) - 调用对象的方法(如
list.Add()前,list 可能为 null)
为什么要做: C# 中多级属性访问时,只要其中一级为 null,就会抛出空引用异常。
对应你代码的示例:
csharp
// 空值保护:防止 Name 为 null 导致 ZhCN 访问失败
info.TypeName = dTOArchive.Name?.ZhCN ?? string.Empty;
如果不做保护 :dTOArchive.Name 为 null 时,dTOArchive.Name.ZhCN 会直接报错。
4. 数据库/缓存操作前的关键数据校验
适用场景:
- 插入/更新数据库前,校验核心字段(如主键、必填字段)
- 依赖其他数据的操作(如你代码中父级分类查询结果)
为什么要做: 核心字段为空会导致数据库操作失败(如主键为空、外键关联失败),依赖数据不存在会导致业务逻辑错误。
对应你代码的示例:
csharp
// 父级分类查询结果校验(必须做)
if (p_info != null)
{
info.ParentID = p_info.ID;
info.ERPParentID = p_info.ERPID;
}
else
{
string errorMsg = $"物料分类【{dTOArchive.Code}】父级数据不存在,父级ERPID:{dTOArchive.Parent}";
LogTextHelper.Error(errorMsg);
throw new Exception(errorMsg);
}
如果不校验 :p_info 为 null 时,p_info.ID 会报错,且会将无效的父级ID插入数据库,导致数据脏污。
5. 可选但建议做的场景
- 集合类型(List/Array):访问
list.Count前,校验list != null - 可空值类型(int?/DateTime?):取值前校验
HasValue - 第三方组件/工具类返回值:如
File.ReadAllText可能返回 null(文件不存在)
二、可以不做空值校验的场景(对比理解)
不是所有场景都需要校验,以下情况可省略:
- 内部可控的常量/初始化对象 :如
var info = new MaterialTypeInfo();后,info 不可能为 null,无需校验。 - C# 8.0+ 的可空引用类型(Nullable Reference Types) :通过
!操作符明确标记非空(如name!),且代码经过严格静态检查。 - 单元测试中的 mock 数据:数据由你完全控制,且仅用于测试。
总结
- 核心原则 :只要数据来源不受代码直接控制 (外部入参、反序列化、数据库/第三方返回),或涉及多级属性访问,必须做空值校验。
- 校验目的:避免空引用异常、保障数据合法性、提前拦截错误(而非让程序崩溃)。
- 最佳实践 :校验时不仅要判断 null,还要结合业务判断"无效值"(如空字符串、0值等,如你代码中的
string.IsNullOrWhiteSpace)。