.NET反射场景下如何创建反射接口的派生类以调用目标方法
问题说明
需要调用反射加载得到的类中的方法,方法入参为同个反射加载类库中定义的接口:
- 被加载类库基于.NET Framework 4.6.1开发,核心结构如下:
public interface InterfaceA{ // 接口成员定义 } public class ClassA { public void MethodA( InterfaceA interfaceParam ) { // 方法逻辑 } }
- 调用方可执行程序基于.NET Framework 4.8开发,已完成的核心反射逻辑如下:
// 加载目标类库 var asm = Assembly.LoadFrom( "...myAssembly.dll" ); // 查找ClassA类型以获取MethodA方法 var classAType = asm.GetExportedTypes().FirstOrDefault( t => t.Name == "ClassA" ); // 实例化ClassA var instance = Activator.CreateInstance(classAType); // 查找目标方法 var method = classAType.GetMethod( "MethodA"); // 查找目标接口 var interfaceAType = asm.GetExportedTypes().FirstOrDefault( t => t.Name == "InterfaceA" ); // 待解决:构造InterfaceA的实现类实例,传入完成方法调用 // method.Invoke( instance, new []{ /* 这里需要InterfaceA类型的实例 */} );
已知InterfaceA的所有成员定义,可实现完全匹配的方法、属性逻辑,但目标类库没有提供可直接实例化的InterfaceA派生类,无法直接创建符合要求的参数实例。
注意:不要尝试在主程序中手动定义同名同成员的InterfaceA再写实现类,即使两个接口的命名空间、名称、成员签名完全一致,只要加载自不同程序集,CLR就会判定为完全不同的类型,传参时会抛出类型不匹配异常。
实现方案
使用.NET Framework原生提供的System.Reflection.Emit能力,在运行时动态生成直接实现反射得到的InterfaceA类型的类,实例化后即可作为合法参数传入。
具体实现步骤:
- 在当前应用程序域中创建动态程序集与动态模块,用于承载运行时生成的实现类
- 在动态模块中定义公共类,添加InterfaceA作为其实现的接口类型
- 遍历InterfaceA的所有成员(方法、属性、事件),为每个成员生成对应的IL实现代码,绑定你预先定义好的业务逻辑
- 完成动态类型创建后,实例化该类型,即可传入
MethodA完成调用
示例代码
以下为最小实现示例,假设InterfaceA包含一个无入参无返回值的方法Execute,需要的执行逻辑是打印一行文本:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; // 前面的反射加载逻辑不变,拿到interfaceAType、instance、method对象 // 1. 初始化动态生成环境 var assemblyName = new AssemblyName("DynamicImplAssembly"); var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicImplModule"); // 2. 定义实现类,标记实现InterfaceA var typeBuilder = moduleBuilder.DefineType( "DynamicInterfaceAImplementation", TypeAttributes.Public | TypeAttributes.Class, null, new Type[] { interfaceAType } ); // 3. 实现InterfaceA的所有方法(示例仅处理方法,属性、事件可按相同逻辑扩展) var interfaceMethods = interfaceAType.GetMethods(); foreach (var methodInfo in interfaceMethods) { // 定义方法,参数、返回值与接口方法完全匹配 var methodBuilder = typeBuilder.DefineMethod( methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual, methodInfo.ReturnType, methodInfo.GetParameters().Select(p => p.ParameterType).ToArray() ); // 生成方法IL逻辑,以下示例逻辑为调用Console.WriteLine输出文本,可替换为自定义业务逻辑 var ilGenerator = methodBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldstr, "InterfaceA方法被动态实现类调用"); ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); ilGenerator.Emit(OpCodes.Ret); // 将定义的方法绑定到接口对应的方法上,完成接口实现 typeBuilder.DefineMethodOverride(methodBuilder, methodInfo); } // 4. 创建动态类型,生成实例 var implType = typeBuilder.CreateType(); var interfaceAInstance = Activator.CreateInstance(implType); // 5. 完成方法调用 method.Invoke(instance, new object[] { interfaceAInstance });
如果接口包含属性,只需要额外为属性生成对应的get/set方法,逻辑和普通方法生成一致;如果需要给动态类传入自定义状态,可以在动态类中定义私有字段、构造方法,实例化时传入需要的上下文参数即可。
这种方式生成的实现类直接和反射加载得到的InterfaceA类型绑定,完全符合CLR类型校验规则,不会出现类型转换错误。
内容的提问来源于stack exchange,提问作者FFMG
相关产品推荐
相关产品推荐

