You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

程序集间高效动态委托调用优化方案咨询

高效调用委托的优化方案(替代DynamicInvoke)

针对你提出的「多次调用同一委托、参数匹配固定、允许首次开销、需降低动态调用损耗」的场景,最优方案是将动态委托转换为强类型委托并缓存编译结果,完全规避DynamicInvoke的动态校验与装箱开销。以下是两种落地实现:

方案1:泛型强类型委托(性能最优)

直接提供泛型版本的调用方法,让调用方传入强类型委托,完全消除动态特性开销,性能接近直接方法调用。

修改后的Invoker类库代码

namespace InvokeLibrary
{
    public static class Invoke
    {
        // 单参数泛型调用方法
        public static void Invoker<T>(Action<T> method, T data)
        {
            for (int i = 0; i < 1000000; i++)
                method(data);
        }

        // 可扩展多参数版本,比如双参数
        public static void Invoker<T1, T2>(Action<T1, T2> method, T1 data1, T2 data2)
        {
            for (int i = 0; i < 1000000; i++)
                method(data1, data2);
        }
    }
}

调用方代码调整

using InvokeLibrary;

var obj = new A("Message from caller");
// 直接传入强类型Action<A>委托
Invoke.Invoker<A>(ToBeInvoked, obj);

void ToBeInvoked(A obj) =>
    Console.WriteLine("A: " + obj.Message);
record A(string Message);

这种方式没有任何装箱拆箱或动态校验,是性能最高的实现,推荐在调用方能提供强类型委托时优先使用。

方案2:表达式树编译+缓存(兼容原有Delegate入参)

如果必须保留原有的Delegate+object[]入参形式,可以通过表达式树将动态委托编译为强类型委托并缓存,首次编译有一次性开销,后续调用性能接近强类型调用。

修改后的Invoker类库代码

namespace InvokeLibrary
{
    public static class Invoke
    {
        // 缓存编译后的强类型委托,避免重复编译
        private static readonly ConcurrentDictionary<Delegate, Delegate> _compiledCache = new();

        public static void Invoker(Delegate method, params object[] data)
        {
            if (_compiledCache.TryGetValue(method, out var compiledDelegate))
            {
                // 根据参数数量分发调用(示例仅处理单参数场景,可扩展多参数)
                ((Action<object>)compiledDelegate)(data[0]);
                return;
            }

            // 构建表达式树:将object参数强转为目标类型,调用原方法
            var methodParams = method.Method.GetParameters();
            var paramExpr = Expression.Parameter(typeof(object));
            var castExpr = Expression.Convert(paramExpr, methodParams[0].ParameterType);
            var callExpr = Expression.Call(Expression.Constant(method.Target), method.Method, castExpr);
            var lambdaExpr = Expression.Lambda<Action<object>>(callExpr, paramExpr);

            // 编译并缓存委托
            var compiled = lambdaExpr.Compile();
            _compiledCache.TryAdd(method, compiled);
            compiled(data[0]);
        }
    }
}

原理说明

  • 首次调用时,通过表达式树生成强类型的Action<object>委托,自动完成参数的类型转换;
  • 用ConcurrentDictionary缓存编译结果,同一委托只会编译一次;
  • 后续调用直接执行编译后的强类型委托,完全避免DynamicInvoke的动态校验、装箱拆箱开销。

性能对比

三种方式的性能排序为:
泛型强类型调用 > 表达式树缓存调用 > Delegate.DynamicInvoke/MethodInfo.Invoke

表达式树缓存方案的首次调用开销约为DynamicInvoke的数倍,但后续百万次调用的性能提升可达10~100倍(具体取决于参数类型与方法逻辑)。

内容的提问来源于stack exchange,提问作者Yoav Haik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:31:02