C#运行时创建泛型Delegate后如何高效调用并获取返回值
解决方案
你可以通过两种常用方案实现高性能的委托调用,避免DynamicInvoke的性能损耗:
方案1:泛型方法中转(适合参数数量固定的场景)
你当前的场景是固定3个入参+1个返回值,完全可以通过泛型方法封装调用逻辑,运行时反射构造对应泛型方法实现调用:
- 首先定义通用泛型调用方法:
private static TResult InvokeFunc<T1, T2, T3, TResult>(Delegate deleg, T1 arg1, T2 arg2, T3 arg3) { // 泛型上下文内可直接强转为对应类型的Func,无性能损耗 var typedFunc = (Func<T1, T2, T3, TResult>)deleg; return typedFunc(arg1, arg2, arg3); }
- 运行时构造泛型方法并调用:
// 构造匹配当前参数/返回值类型的封闭泛型方法,该MethodInfo可缓存复用 var invokeMethod = typeof(/* 填入上述方法所在类的类型 */) .GetMethod(nameof(InvokeFunc), BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(type1, type2, type3, returnType); // 执行调用 var result = invokeMethod.Invoke(null, new object[] { deleg, val1, val2, val3 });
注意:构造好的invokeMethod建议全局缓存,避免每次调用重复反射构造,缓存后调用性能接近原生委托
方案2:表达式树编译调用器(适合参数数量不固定的场景)
如果参数数量也需要动态适配,可以通过表达式树编译出通用调用委托,后续直接复用:
// 定义表达式参数 var delegParam = Expression.Parameter(typeof(Delegate), "deleg"); var argsParam = Expression.Parameter(typeof(object[]), "args"); // 将基类Delegate强转为实际生成的委托类型 var castedDeleg = Expression.Convert(delegParam, delegMethodType); // 构造参数列表:将object数组中的每个元素转为对应参数类型 var callParameters = new Expression[3]; callParameters[0] = Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(0)), type1); callParameters[1] = Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(1)), type2); callParameters[2] = Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(2)), type3); // 构造委托调用表达式 var callExpr = Expression.Invoke(castedDeleg, callParameters); // 将返回值转为object统一返回 var resultExpr = Expression.Convert(callExpr, typeof(object)); // 编译为通用调用委托,该委托可全局缓存复用 var invoker = Expression.Lambda<Func<Delegate, object[], object>>(resultExpr, delegParam, argsParam).Compile();
后续调用直接使用编译好的invoker即可:
var result = invoker(deleg, new object[] { val1, val2, val3 });
该方案的性能损耗仅存在于表达式树编译阶段,缓存后的调用性能和原生委托差异极小,比DynamicInvoke快数十倍
内容的提问来源于stack exchange,提问作者JaimeCamargo
相关产品推荐
相关产品推荐

