C#编译器可空分析:为何null条件访问时判定myObj可能为null
问题描述
编写如下代码时,编译器在ValidateMyObj(myObj)调用位置提示myObj可能为null。按照代码逻辑,只有当myObj不为null时,string.IsNullOrEmpty才会返回false并进入条件分支,但编译器仍判定它可能为null;同时同一条件分支内后续的myObj.Exists赋值位置,编译器又判定myObj不可能为null,行为存在矛盾。
var myObj = myObjCol.FirstOrDefault(x => x.Member); if (!string.IsNullOrEmpty(myObj?.StrProp)) { ValidateMyObj(myObj); if (ViewBag.IsValid) myObj.Exists = true; }
补充验证测试
显式null检查测试
即便手动添加显式null判断,编译器仍表现出相同的告警行为:
var myObj = myObjCol.FirstOrDefault(x => x.Member); if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp)) { ValidateMyObj(myObj); if (ViewBag.IsValid) myObj.Exists = true; }
通过关闭?运算符反编译功能的ILSpy查看,上述代码实际编译为如下逻辑:
MyObjType myObj = myObjCol.FirstOrDefault(x => x.Member); if (myObj != null && !string.IsNullOrEmpty((myObj != null) ? myObj.StrProp : null)) { ValidateMyObj(myObj); if (ViewBag.IsValid) myObj.Exists = true; }
移除null条件运算符测试
如果移除null条件运算符?.,编译器的空值告警波浪线就会消失:
var myObj = myObjCol.FirstOrDefault(x => x.Member); if (myObj != null && !string.IsNullOrEmpty(myObj.StrProp)) { ValidateMyObj(myObj); if (ViewBag.IsValid) myObj.Exists = true; }
同时移除显式null检查和null条件运算符测试
如果同时移除显式null检查和null条件运算符,告警也会消失,且传入IsNullOrEmpty的参数也不会触发空值告警:
var myObj = myObjCol.FirstOrDefault(x => x.Member); if (!string.IsNullOrEmpty(myObj.StrProp)) { ValidateMyObj(myObj); if (ViewBag.IsValid) myObj.Exists = true; }
原因说明
这个现象是C#可空引用类型静态流分析的既定实现规则导致的:
- 编译器不会从
myObj?.StrProp的非空结果,反向推导出myObj本身非空。null条件运算符?.的设计目的就是兼容接收者为null的场景,编译器在分析包含?.的表达式时,只会记录表达式本身的可空状态,不会把表达式执行过程中“接收者不为null”的临时判断结果,保留为表达式执行完成后对应变量的可空状态。哪怕你在条件中提前写了myObj != null的判断,只要后续同个条件里用?.访问了myObj,编译器就会保守地重置myObj的可空状态为“可能为null”,因此进入分支后第一行调用ValidateMyObj(myObj)时会触发告警。 - 当代码执行到
myObj.Exists = true的位置时,编译器会识别到这里直接对myObj做了成员访问——这种写法本身就隐含了“myObj不可能为null”的前提(否则运行时会抛出空引用异常),因此流分析会在这个位置将myObj的状态更新为“非空”,不会再触发告警。 - 当你移除
?.直接访问myObj.StrProp时,编译器会在条件判断访问StrProp的位置就识别到myObj非空,这个状态会被保留到整个条件分支内,因此全程不会出现空值告警。
注:这是编译器静态分析的保守实现策略,不代表代码实际运行时会出现空引用,只是编译器无法通过现有分析规则100%确认变量非空。如果要消除告警,可以在进入分支后先加一个
Debug.Assert(myObj != null)的断言,或者直接把null判断和属性访问拆开写,避免在条件判断中对已经判空的变量使用?.运算符。
内容的提问来源于stack exchange,提问作者bkqc
相关产品推荐
相关产品推荐

