.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
相关产品推荐
相关产品推荐

