可空性检查:引用与值类型差异——为何object?判空后可用,long?却不行?
可空引用类型与可空值类型的空值检查差异解析
这不是编译器忽略了非空状态,而是可空引用类型(NRT)和可空值类型(Nullable
1. 为什么object?场景能正常编译
可空引用类型是C# 8.0引入的编译期注解机制,object?本质还是object,只是给编译器打了“这个变量可能为null”的标记。当你通过if(obj is null)排除null后,编译器的流分析会标记该变量为非空状态,自动把object?“提升”为object,所以能直接传给要求非空object的方法。
2. 为什么long?场景会报错
可空值类型long?是Nullable<long>结构体的语法糖,它本质是一个包含long Value和bool HasValue的包装类型,和long是完全不同的类型。哪怕你通过if(literal is null)确认了它不为空,变量的类型依然是Nullable<long>,编译器不会自动解包成long——因为这涉及运行时的类型转换(从包装结构体提取内部值),编译器不会替你做这个隐式转换。
解决方法
三种常用处理方式:
方式一:模式匹配直接提取值
把null检查和值提取合并,直接得到long类型变量:
private static void TestWithLiteral() { long? literal = ReturnsOptionalLiteral(); if(literal is long value) { FunctionUsingLiteral(value); } else { Console.WriteLine("Literal is null. Returning."); return; } }
方式二:非空断言运算符!
既然已经确认变量非空,用!告诉编译器“我确定这个变量不为空”,强制转换为long:
private static void TestWithLiteral() { long? literal = ReturnsOptionalLiteral(); if(literal is null) { Console.WriteLine("Literal is null. Returning."); return; } FunctionUsingLiteral(literal!); // 编译通过 }
方式三:直接访问Value属性
已确认literal不为空,访问Value不会抛出InvalidOperationException:
private static void TestWithLiteral() { long? literal = ReturnsOptionalLiteral(); if(literal is null) { Console.WriteLine("Literal is null. Returning."); return; } FunctionUsingLiteral(literal.Value); // 直接取内部值 }
总结
可空引用类型是编译期的null安全注解,编译器能通过流分析自动处理类型提升;可空值类型是运行时的包装结构体,必须显式解包才能获取内部值——这就是两种场景行为不同的核心原因。
内容的提问来源于stack exchange,提问作者user3227340
相关产品推荐
相关产品推荐

