C#中is not null空检查为何对可空结构体无效?如何解决?
结论
这不是C#设计bug,是混淆了可空引用类型和可空值类型的底层实现规则导致的用法错误。
原理说明
- 类是引用类型,
Foo?和Foo在运行时是完全相同的类型,?只是给编译器加的空状态追踪标记。通过is not null检查后,编译器会直接把该变量的空状态标记为非空,允许直接访问Foo的成员;!null包容运算符的作用就是告诉编译器跳过该引用的空检查,因此对引用类型生效。 - 结构体是值类型,
Bar?本质不是加了注解的Bar,是框架定义的独立泛型结构体Nullable<Bar>,它是对Bar实例的包装,本身没有定义你在Bar里写的a字段。编译器对Nullable<T>做is not null判断时,仅能确认它的HasValue属性为true,不会自动解包把变量类型从Nullable窄化为Bar ,所以直接点a必然报错。!运算符只对可空引用类型的空状态追踪生效,不会帮你解包Nullable值类型,加了!之后变量还是Nullable<Bar>类型,自然访问不到Bar的成员。
最短兼容写法
兼容性最高、最短的写法是空检查通过后显式访问Nullable类型的Value属性,不需要额外声明变量:
Bar? bar = new Bar { a = "bar" }; if (bar is not null) { Console.WriteLine(bar.Value.a); // 正常编译运行 }
如果不想每次都写.Value,可以用C# 8+支持的属性模式匹配,在判断的同时直接解包拿到非空的Bar实例:
Bar? bar = new Bar { a = "bar" }; if (bar is {} nonNullBar) { Console.WriteLine(nonNullBar.a); // 正常编译运行 }
内容的提问来源于stack exchange,提问作者w123
相关产品推荐
相关产品推荐

