c# 在高并发场景下,反射的性能瓶颈和优化方法

来源:这里教程网 时间:2026-02-21 17:37:51 作者:

反射调用
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<MyService, int, string>)Delegate.CreateDelegate(
    typeof(Func<MyService, int, string>), 
    null, 
    method);
// 后续直接调用,无反射开销
var result = func(instance, 42);
必须确保
method
是静态或实例方法,并与委托签名严格匹配(含
this
参数位置)
不能用于泛型方法的开放构造(如
List<t>.Add</t>
),需先用
MakeGenericMethod
闭合
委托缓存要线程安全:建议用
Lazy<t></t>
ConcurrentDictionary
存储

Expression.Lambda
编译动态委托适合复杂绑定场景

当参数需运行时映射(如从

IDictionary<string object></string>
构建调用参数)、或涉及属性访问/转换时,
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<Func<object, object, string>>(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 生成,彻底消灭运行时反射

反射不是不能用,而是不能“裸用”。高并发下,任何未缓存、未编译、未静态化的反射操作,都会成为吞吐量的隐形天花板。

相关推荐