You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

可空性检查:引用与值类型差异——为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:55:02