调用PropertyInfo.GetValue获取Nullable<T>属性返回实际值的原因
核心原因
这个现象本质是CLR对Nullable<T>的专属装箱规则导致的,和PropertyInfo.GetValue本身的实现没有直接关系。PropertyInfo.GetValue的返回值类型是object,所有值类型在返回前都会经历装箱过程转换为引用类型。而CLR对Nullable<T>的装箱逻辑做了硬编码的特殊处理:
- 当
Nullable<T>实例的HasValue为false时,装箱结果直接是null引用 - 当
Nullable<T>实例的HasValue为true时,装箱时不会把整个Nullable<T>结构体打包,而是直接提取内部存储的Value字段的值,对这个T类型的值做装箱返回
也就是说运行时层面根本不存在装箱后的Nullable<T>对象,你自然不可能通过返回object的方法拿到这种实例。你可以用非常简单的代码验证这个规则,完全不需要用到反射:
int? testValue = 100; object boxedResult = testValue; // 输出结果是System.Int32,而非Nullable<Int32> Console.WriteLine(boxedResult.GetType()); int? nullValue = null; object boxedNull = nullValue; // 输出True,装箱结果直接是空引用 Console.WriteLine(boxedNull == null);
反射调用GetValue只是刚好触发了这个装箱流程而已,不是反射单独给Nullable做了特殊判断。
是不是.NET内置的专属机制
是,这个逻辑是写在CLR核心层面的硬编码规则,仅对System.Nullable<T>这一个泛型类型生效,没有任何公开的扩展入口。
你自己定义的结构体哪怕完全复刻Nullable<T>的结构,包含HasValue属性和对应的值字段,装箱时也会走普通值类型的装箱逻辑,把整个结构体实例打包到托管堆上,不会出现自动拆出内部值的行为。
自定义类型能否实现相同效果
不能。
这种装箱层面的行为是运行时内置的,既不支持通过特性、接口实现,也不支持自定义类型注册类似的转换逻辑。如果你只是想在自己的业务反射逻辑里达到类似的效果,只能手动写一层包装判断:当识别到目标类型是你自定义的包装类型时,主动提取其内部封装的实际值返回,但这属于上层业务逻辑的自定义处理,和CLR原生的Nullable特殊机制完全不是一个层面的实现。
内容的提问来源于stack exchange,提问作者Krisztián Szilágyi
相关产品推荐
相关产品推荐

