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

.NET Core 如何构建表达式树调用泛型接口方法替代Method.Invoke

.NET Core 3.1 高性能动态方法调用解决方案

一、表达式树报错根因及修复

你调用Expression.Call报错的核心原因是:HandleMessageResultAsync是IEncapsulatedMessageHandlerV2<,>接口的显式实现方法,这类方法不会作为实现类的公开实例方法暴露,所以你通过typeof(HandlerV2).GetMethod("HandleMessageResultAsync", BindingFlags.Instance | BindingFlags.Public)拿到的method变量是null,自然会触发参数异常。

修复思路是先从接口类型获取方法信息,再构建调用表达式,完整可运行代码如下:

// 建议将编译后的委托静态缓存,避免重复编译开销
static Func<object, Message, Task<object>> BuildHandlerDelegate()
{
    var handlerType = typeof(HandlerV2);
    // 匹配对应的闭合泛型接口类型
    var interfaceType = handlerType.GetInterfaces()
        .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEncapsulatedMessageHandlerV2<,>));
    // 从接口类型获取显式实现的方法
    var method = interfaceType.GetMethod("HandleMessageResultAsync");

    // 构建表达式树
    var instParam = Expression.Parameter(typeof(object), "instance");
    var msgParam = Expression.Parameter(typeof(Message), "message");
    // 将实例转换为接口类型
    var instCast = Expression.Convert(instParam, interfaceType);
    // 将消息转换为对应泛型参数类型
    var msgCast = Expression.Convert(msgParam, interfaceType.GetGenericArguments()[0]);
    // 调用接口方法
    var callExpr = Expression.Call(instCast, method, msgCast);
    // 将返回值转换为object兼容通用场景
    var resultCast = Expression.Convert(callExpr, typeof(object));

    return Expression.Lambda<Func<object, Message, Task<object>>>(resultCast, instParam, msgParam).Compile();
}

// 调用示例
static TResponse UsingExpressionTree<TResponse>()
{
    var cachedDelegate = BuildHandlerDelegate();
    var instance = Activator.CreateInstance(typeof(HandlerV2));
    var resultTask = (Task<TResponse>)cachedDelegate(instance, new MyMessageType()).GetAwaiter().GetResult();
    return resultTask.GetAwaiter().GetResult();
}

缓存后调用性能接近原生委托,是Method.Invoke的10~50倍。

二、其他高性能动态调用方案

1. Delegate.CreateDelegate 方案(代码最简)

不需要写复杂的表达式树,直接用.NET内置的委托创建方法,性能和编译后的表达式树完全一致:

static TResponse UsingCreateDelegate<TResponse>()
{
    var interfaceType = typeof(IEncapsulatedMessageHandlerV2<MyMessageType, MyMessageResult>);
    var method = interfaceType.GetMethod("HandleMessageResultAsync");
    var instance = Activator.CreateInstance(typeof(HandlerV2));
    // 生成强类型委托,建议缓存复用
    var del = (Func<IEncapsulatedMessageHandlerV2<MyMessageType, MyMessageResult>, MyMessageType, Task<MyMessageResult>>)
        Delegate.CreateDelegate(typeof(Func<IEncapsulatedMessageHandlerV2<MyMessageType, MyMessageResult>, MyMessageType, Task<MyMessageResult>>), method);
    
    var task = del((IEncapsulatedMessageHandlerV2<MyMessageType, MyMessageResult>)instance, new MyMessageType());
    return (TResponse)(object)task.GetAwaiter().GetResult();
}

2. Source Generator 方案(性能最高)

.NET Core 3.1及以上版本支持源生成器,可以在编译期扫描所有EncasulatedMessageHandlerV2<,>的实现类,自动生成静态调用代码,完全无运行时反射开销,性能和手写静态调用完全一致。

3. 修复后的dynamic调用方案

你之前用dynamic无法调用显式接口方法,只要先将实例转换为对应接口类型即可:

static TResponse UsingDynamicFixed<TResponse>()
{
    var instance = Activator.CreateInstance(typeof(HandlerV2));
    // 先转接口再转dynamic即可访问显式实现的方法
    var tsk = (Task<TResponse>)((dynamic)(IEncapsulatedMessageHandlerV2<MyMessageType, MyMessageResult>)instance)
        .HandleMessageResultAsync(new MyMessageType());
    return tsk.GetAwaiter().GetResult();
}

dynamic性能比Method.Invoke高3~5倍,适合调用频次不高的场景。


性能排序参考

静态调用 ≈ 源生成器 > 预编译委托(表达式树/CreateDelegate) > dynamic > Method.Invoke

内容的提问来源于stack exchange,提问作者Om Rudi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 16:54:04