MethodInfo.Invoke 是高并发下的主要性能瓶颈,因其触发完整反射解析流程、引发高频堆分配与 GC 压力;推荐用 Delegate.CreateDelegate 或 Expression.Compile 缓存强类型委托以提升 10–30 倍性能。

反射调用 MethodInfo.Invoke 是高并发下的主要性能瓶颈
在 Web API 或高频服务中,直接用 MethodInfo.Invoke 调用方法,会触发完整的反射解析流程:类型检查、参数绑定、访问权限校验、装箱/拆箱(值类型)、异常包装。单次调用开销约是直接调用的 50–100 倍;并发量上来后,GC 压力和 CPU 竞争会急剧放大。
- 每次
Invoke都新建object[]参数数组,引发堆分配 - 泛型方法或带
ref/out参数时,绑定逻辑更重,容易抛TargetParameterCountException - .NET 6+ 中 JIT 对反射调用的内联完全失效,无法通过 AOT 缓解
用 Delegate.CreateDelegate 替代 Invoke 可提速 10–30 倍
将反射获取的 MethodInfo 编译为强类型委托,后续调用就等同于原生方法调用。关键在于:委托只创建一次,复用在所有请求中。
var method = typeof(MyService).GetMethod("Process");
// 推荐:指定委托类型,避免 object[] 拆包
var func = (Func)Delegate.CreateDelegate(
typeof(Func),
null,
method);
// 后续直接调用,无反射开销
var result = func(instance, 42);
- 必须确保
method是静态或实例方法,并与委托签名严格匹配(含this参数位置) - 不能用于泛型方法的开放构造(如
List),需先用.Add MakeGenericMethod闭合 - 委托缓存要线程安全:建议用
Lazy或ConcurrentDictionary存储
Expression.Lambda 编译动态委托适合复杂绑定场景
当参数需运行时映射(如从 IDictionary 构建调用参数)、或涉及属性访问/转换时,Expression 比 CreateDelegate 更灵活,且编译后性能几乎无损。
var instanceParam = Expression.Parameter(typeof(object), "instance");
var argParam = Expression.Parameter(typeof(object), "arg");
var convertedArg = Expression.Convert(argParam, typeof(int));
var call = Expression.Call(
Expression.Convert(instanceParam, typeof(MyService)),
typeof(MyService).GetMethod("Process"),
convertedArg
);
var lambda = Expression.Lambda>(call, instanceParam, argParam);
var compiled = lambda.Compile(); // 一次性编译
// 复用 compiled
var result = compiled(serviceObj, 42);
- 首次编译耗时较高(毫秒级),但之后调用接近直接调用
- 注意表达式树中类型转换错误会导致
InvalidOperationException,应在初始化阶段验证 - 避免在热路径里反复调用
Compile(),它不可缓存且触发 JIT
真正需要避免的是「每次请求都反射」
很多框架(如早期 ASP.NET Core Model Binding)会在每次请求中重新获取 PropertyInfo、调用 GetValue,这比方法调用更糟——属性访问还涉及索引器、get_ 方法查找、BindingFlags 过滤等额外步骤。
- 把
PropertyInfo和对应 getter/setter 委托缓存在静态字典里,键可为(Type, PropertyName) - 用
System.Reflection.Metadata+MetadataReader在启动时预扫描类型,生成轻量元数据缓存,绕过运行时反射 API - 对核心高频对象(如 DTO),直接手写映射代码或用 Source Generator 生成,彻底消灭运行时反射
反射不是不能用,而是不能“裸用”。高并发下,任何未缓存、未编译、未静态化的反射操作,都会成为吞吐量的隐形天花板。











