getproperties()返回空数组主因是bindingflags未配对;默认仅返回public实例属性,需显式添加nonpublic、static、flattenhierarchy等标志;泛型类反射须用makegenerictype构造封闭类型,继承属性需手动遍历基类。

GetProperties() 返回空数组的常见原因
反射拿不到属性,大概率不是代码写错,而是访问级别或泛型/继承干扰导致。默认 GetProperties() 只返回 public 实例属性,且不递归基类——哪怕你写了 virtual 或 override,父类的 public 属性也不会自动出现。
- 要包含非 public 属性(如
private、protected),必须显式传入BindingFlags:typeof(MyClass).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - 要包含基类属性,得手动向上遍历:
type.BaseType?.GetProperties(...),.NET 没有内置“全继承链属性”快捷方法 - 泛型类实例(如
MyClass<string></string>)反射时要用typeof(MyClass)获取泛型定义,再调用MakeGenericType(...)构造封闭类型,否则GetProperties()会失败或返回空
Activator.CreateInstance() 创建泛型实例的坑
直接传泛型类型参数给 Activator.CreateInstance() 很容易报 ArgumentException: Type must be a runtime type,因为没区分开放泛型和封闭泛型。
- 开放泛型类型(如
typeof(List))不能直接实例化,必须先构造封闭类型:typeof(List).MakeGenericType(typeof(int)) - 带构造参数的泛型类(如
class Repo<t> { public Repo(IOptions<t> opts) {...} }</t></t>),不能靠Activator.CreateInstance(type, args)自动解析依赖,它不走 DI 容器逻辑——此时应改用IServiceProvider.GetService(type) - 性能敏感场景慎用
Activator.CreateInstance():它比直接 new 慢 5–10 倍。高频创建建议缓存ConstructorInfo.Invoke()或预编译表达式树
PropertyInfo.SetValue() 报 TargetException 的真实含义
TargetException 并不表示属性不存在,而是目标对象与属性所属类型不匹配——比如你用 obj.GetType().GetProperty("Name") 拿到的是 Person.Name 的 PropertyInfo,却试图对一个 Employee 实例(虽继承自 Person)调用 SetValue(),而该属性在 Employee 中被重写为 new 字段,则会失败。
- 检查属性是否是
virtual且被override:此时应通过obj.GetType().GetProperty("Name")(而非基类类型)获取,确保类型一致 - 值类型属性赋值时注意装箱:对
int属性设null会抛异常,需先判断property.PropertyType.IsNullable()(辅助方法需自行实现) - 只读属性(只有
get)调用SetValue()会静默失败(不报错但无效果),可用property.CanWrite提前校验
动态创建对象后如何安全绑定 JSON 数据
用反射创建实例后接 System.Text.Json 反序列化,常因属性名大小写、忽略只读字段等策略不一致导致绑定失败。
- 不要用
JsonSerializer.Deserialize<object>(json)</object>——它不会还原为你的目标类型,而是变成JsonElement或Dictionary<string object></string> - 正确做法是:先用
Activator.CreateInstance(targetType)创建空实例,再用JsonSerializer.Populate()注入数据(需启用PropertyNameCaseInsensitive = true) - 若目标类含
[JsonIgnore]或[JsonPropertyName("xxx")],Populate()默认不识别这些特性,得传入自定义JsonSerializerOptions并设置IgnoreReadOnlyProperties = false等选项
反射链越长(类型推导 → 属性遍历 → 值设置 → 序列化注入),出问题的位置就越隐蔽。别依赖“看起来能跑”,每个环节都要验证 PropertyInfo 是否来自预期类型、CanWrite 是否为 true、IsAssignableFrom() 是否成立——这些检查加起来不到三行,却能省掉半天调试时间。










