调用 PropertyInfo.SetValue 前必须检查 propertyInfo.CanWrite,否则可能抛 TargetException 或静默失败;需处理 init 属性限制、private set 强制访问风险、值类型与 null 兼容性、BindingFlags 组合合理性、属性发现调试及高频场景下的缓存或表达式树优化。

用 PropertyInfo.SetValue 设置属性值前必须确认可写性
直接调用 SetValue 报错 TargetException 或静默失败,往往是因为目标属性没有 public set 访问器,或为只读自动属性(C# 6+ 的 get; init; 在运行时也视为不可写)。反射不会绕过语言访问规则。
实操建议:
- 先检查
propertyInfo.CanWrite,为false时跳过或抛出明确异常 - 对
init属性,需通过构造函数或Activator.CreateInstance传参初始化,不能后期用SetValue修改 - 若属性是 private set,但你确定类型安全且需强制写入,可用
BindingFlags.NonPublic | BindingFlags.Instance获取该 setter 方法并 invoke,但不推荐用于常规业务逻辑
处理值类型与 null 值时注意装箱和可空类型转换
给 int、DateTime 等值类型属性赋 null,或把 int? 的 null 传给非空 int 属性,会触发 ArgumentException:“Object of type 'null' cannot be converted to type 'System.Int32'”。
实操建议:
- 用
Nullable.GetUnderlyingType(propertyInfo.PropertyType)判断是否为可空类型,再决定是否允许null传入 - 对非空值类型,确保传入的值不是
null;若来源不确定,先做Convert.ChangeType(value, propertyInfo.PropertyType)转换,它比直接 cast 更健壮 - 避免用
value as T强转,尤其在泛型上下文中,容易因类型擦除导致意外null
批量设置属性时慎用 BindingFlags.Public | BindingFlags.Instance
只写这两个 flag 看似稳妥,但会漏掉 internal、protected 属性,甚至在某些程序集可见性限制下连 public 属性都获取不到(比如跨 assembly 且未声明 [InternalsVisibleTo])。
实操建议:
- 按实际需求组合 flag:
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance是最常用组合,覆盖绝大多数场景 - 若目标类型来自第三方库且属性是 internal,确认对方是否开放了
[InternalsVisibleTo("YourAssemblyName")] - 用
type.GetProperties(BindingFlags)后,建议加日志输出匹配到的属性名和可写性,便于调试“为什么某个属性没被设上值”
性能敏感场景下避免重复反射调用
每次调用 GetProperty + SetValue 都有明显开销,尤其在循环中对成百上千对象设同一批属性时,CPU 时间可能翻倍。
实操建议:
- 提前缓存
PropertyInfo实例,例如用static readonly Dictionary存储已解析的属性列表PropertyCache - 更进一步:用
Expression.Lambda编译一次性的 setter 委托(如Action),首次慢,后续调用接近原生速度 - 不要为单次操作(如 DTO 映射一次)过度优化;只有在高频、确定瓶颈在反射时,才引入缓存或表达式树
反射设值本身不难,难的是判断“这个值到底能不能设、该用什么方式设、设完有没有副作用”。很多问题出在假设属性可写、类型兼容、作用域可见——而这些恰恰是编译器帮你挡掉的,反射却要求你亲手验证。










