反射需谨慎使用:热路径应缓存委托而非反复Invoke;字符串加载类型须遍历程序集并判空;GetMethod需指定参数类型避免重载歧义;GetValue前检查CanRead,性能敏感场景必须缓存委托。

反射不是万能的,但不用它,很多动态场景根本没法落地——比如插件系统、序列化框架、ORM 的属性映射。关键在于:别在热路径上用 GetMethod + Invoke 反复查、反复调;要用就缓存委托。
怎么安全地获取类型并避免 NullReferenceException
直接写 typeof(MyClass) 最稳,但如果你是从字符串加载类型(比如配置里写的类名),Type.GetType 很容易返回 null:它只查当前程序集和 mscorlib,不搜引用的其他程序集。
- 用
Assembly.GetExecutingAssembly().GetType("Namespace.MyClass")或遍历所有已加载程序集:AppDomain.CurrentDomain.GetAssemblies()逐个调assembly.GetType(fullTypeName) - 检查返回值是否为
null再继续,别假设一定存在——尤其跨版本或插件未部署时 - 如果类型名带泛型(如
List`1),得用typeof(List).FullName.Replace("`1", "`1")这种方式拼,或用Type.MakeGenericType
调用方法前,先确认签名匹配,别硬传参数
GetMethod("Save") 可能拿到多个重载,不指定参数类型的话,.NET 默认返回第一个(通常是无参版),结果运行时报 TargetParameterCountException。
- 显式传入参数类型数组:
type.GetMethod("Save", new[] { typeof(string), typeof(int) }) - 注意值类型要装箱,引用类型别传
null给非空约束参数,否则TargetInvocationException包着原始异常抛出来,堆栈难定位 - 静态方法调用时,
Invoke第一个参数传null;实例方法必须传真实对象,不能传Activator.CreateInstance(type)后不检查构造失败
为什么 PropertyInfo.GetValue 常返回 null 却不报错?
因为属性 get 访问器可能返回 null,也可能内部抛异常——而 GetValue 默认把异常包装成 TargetInvocationException,但如果你没开调试器或没 catch,就只看到 null,误以为字段没值。
- 用
property.CanRead先判断可读性,只对只写属性调GetValue必然出问题 - 访问器里有逻辑(比如懒加载、计算属性),要确保目标对象状态合法;例如访问
DbContext.Entry(x).CurrentValues前,x 得是被跟踪实体 - 性能敏感场景别反复调
GetValue,用Delegate.CreateDelegate编译成强类型委托缓存起来,比如Func<object object></object>
最常被忽略的是:反射获取到的 MethodInfo、PropertyInfo 等对象本身不轻量,但它们的 Invoke 和 GetValue 是重量级操作——每次都要做安全检查、参数封送、堆栈展开。缓存委托不是“优化建议”,是必要动作。


















