getproperty 和 getvalue 返回 null 的常见原因是属性不可访问(如 private/protected)或对象实例为 null;反射默认只查找 public 成员,private 属性需显式指定 bindingflags.nonpublic | bindingflags.instance,且必须确保目标对象非 null。

用 GetProperty 和 GetValue 读取属性值时,为什么返回 null?
常见原因是属性不可访问(private/protected)或对象实例为 null。反射默认只查 public 成员,private 属性必须显式指定 BindingFlags。
- 读取 private 属性:调用
GetProperty("Name", BindingFlags.NonPublic | BindingFlags.Instance) - 确保目标对象非
null,否则GetValue直接抛TargetException - 若属性有 getter 但逻辑抛异常(如未初始化的 lazy 字段),
GetValue会包装成TargetInvocationException,需捕获其InnerException - 泛型类的属性名不带泛型参数,例如
List<int>.Count</int>的属性名就是"Count",无需写"Count<int>"</int>
设置属性值前,必须检查 CanWrite 吗?
不是“必须”,但跳过它大概率导致运行时报 InvalidOperationException 或静默失败——尤其面对只读自动属性(get; init;)、readonly 字段模拟的属性、或有 setter 权限控制的属性。
-
PropertyInfo.CanWrite返回false时,SetValue一定失败(除非用BindingFlags.SetField强制写 backing field,但这已不属于“设属性”范畴) - 对于
init属性,.NET 5+ 中CanWrite为true仅在对象构造阶段;反射调用SetValue会触发NotSupportedException - 值类型属性(如
int)赋null会触发ArgumentException,需先判断PropertyType.IsNullable()或做装箱处理
GetProperty 找不到重载属性怎么办?
C# 不支持属性重载,所谓“找不到”通常是名字拼错、大小写不符,或误把索引器当属性。索引器在反射中是特殊方法(get_Item),不是 PropertyInfo。
- 确认属性真实名称:用
type.GetProperties()全量打印,别依赖 IDE 提示(可能被隐藏) - 忽略大小写查找:用
BindingFlags.IgnoreCase,但注意这会降低性能且可能匹配到意外成员 - 索引器访问应改用
type.GetProperty("Item")(如果定义了名称为Item的索引器),再调GetValue(obj, new object[]{index}) - VB.NET 编译的程序集可能有隐藏的属性名修饰(如
$PrivateImpl$...),此时需用GetRuntimeProperties()(System.Reflection扩展)并过滤IsSpecialName == false
频繁反射读写属性,性能瓶颈在哪?
主要开销不在 GetProperty 查找本身(可缓存 PropertyInfo),而在于每次 GetValue/SetValue 的安全检查、装箱/拆箱、以及 JIT 对动态调用路径的优化缺失。
- 缓存
PropertyInfo实例比反复调用GetProperty快 3–5 倍;更进一步,用Delegate.CreateDelegate创建强类型 getter/setter 委托,性能可接近直接访问 - 对同一类型同一属性,避免重复创建委托;可用
ConcurrentDictionary<type concurrentdictionary func object>>></type>缓存 - .NET 6+ 推荐用
System.Reflection.Metadata+ 源生成器预生成访问器,彻底规避运行时反射开销——但这是编译期方案,不适用于完全动态的场景 - 调试模式下反射慢得多(额外校验),发布模式差异缩小,但依然比直接访问慢一个数量级
实际项目里最易被忽略的是属性 setter 的副作用——比如触发 INotifyPropertyChanged、校验逻辑或状态同步。反射调用它和代码里直接点 .Prop = val 效果一致,该抛异常照样抛,该刷新 UI 一样刷新。别把它当成“绕过业务逻辑”的捷径。










