使用PropertyInfo.GetValue前必须确保实例非null,静态属性传null,实例属性传有效对象;需正确设置BindingFlags以匹配属性访问级别和类型;返回值为object,值类型需谨慎转换;建议缓存PropertyInfo或使用表达式树提升性能。

用 PropertyInfo.GetValue 获取属性值前必须确保实例非 null
反射读属性最常崩在 NullReferenceException,不是因为反射写错了,而是传给 GetValue 的对象实例是 null。比如你用 typeof(Person).GetProperty("Name").GetValue(obj),只要 obj 是 null,立刻炸——哪怕属性本身是静态的,也得看调用方式。
- 静态属性必须传
null作为第一个参数:prop.GetValue(null) - 实例属性必须传有效实例:
prop.GetValue(instance),否则抛异常 - 泛型类型或嵌套对象容易漏掉初始化,建议加空值检查:
if (instance == null) throw new ArgumentException("实例不能为空") - 使用
BindingFlags时若没指定Instance或Static,可能查不到属性,导致GetProperty返回null,后续调用GetValue会 NRE
获取只读属性(没有 set)或私有属性要配对 BindingFlags
默认 GetProperty 只找 public 实例属性。想读 private readonly string _id 或 public string FullName { get; },不显式声明绑定标志就查不到,返回 null,接着调 GetValue 就崩。
- 读私有字段支撑的属性:加
BindingFlags.NonPublic | BindingFlags.Instance - 读只读自动属性(C# 6+ 的
get;):同样需要BindingFlags.Public | BindingFlags.Instance,它本质仍是 public 属性 - 混用 static 和 instance 会冲突,不能同时设
BindingFlags.Static | BindingFlags.Instance - 如果类在其他程序集且属性是 internal,还得确保调用方有
[InternalsVisibleTo]授权,否则NonPublic也拿不到
GetValue 返回 object,值类型要小心装箱和 null 判断
无论属性是 int、DateTime 还是 string,GetValue 统一返回 object。对可空值类型(如 int?)或引用类型为 null 的情况,直接强转会出错。
- 别写
(int)prop.GetValue(obj)—— 如果值是null或类型不匹配,抛InvalidCastException - 安全做法:用
prop.PropertyType.IsValueType && Nullable.GetUnderlyingType(prop.PropertyType) == null判断是否为非空值类型,再决定是否允许直接转换 - 更通用的是用
Convert.ChangeType,但它对自定义类型或复杂结构可能失败,慎用于生产环境高频路径 - 属性是
Nullable且当前值为null时,GetValue返回的就是null,不是default(T?),这点和字段反射一致
性能敏感场景下避免重复反射调用
每次调 GetProperty + GetValue 都触发元数据查找和权限检查,比直接访问慢 50–100 倍。循环里反复反射同一个属性,CPU 会明显发热。
- 把
PropertyInfo缓存起来,比如用ConcurrentDictionary> - 不要在 foreach 中每次都
typeof(T).GetProperty("X"),提前提取并复用 - 高吞吐服务中,考虑用表达式树编译成委托(
Expression.Lambda+Compile()),首次慢、后续接近原生速度 - .NET 5+ 可用
ReflectionContext或第三方库如FastMember,但要注意它绕过部分安全检查,测试环境和生产行为需一致
反射不是黑魔法,它是运行时查表+调用。属性名拼错、访问级别锁死、实例为空、类型转换越界——这些问题不会因为用了反射就消失,只是报错位置变远了。真要靠反射干活,就得把每一步的契约(null 怎么办、权限够不够、类型稳不稳)都当接口文档来读。










