如何将Func委托转换为IL代码并在动态生成程序集中执行?
解决方案
要解决运行时将Func<object[], object>委托转换为IL并在生成程序集中执行的问题,核心思路是将原委托方法的IL指令复制到生成的动态类型方法中,而非直接引用原方法(避免跨程序集依赖问题)。以下是具体实现步骤和代码:
1. 依赖准备
首先安装Mono.Reflection NuGet包,用于读取原方法的IL指令。
2. 实现核心方法
using Mono.Reflection; using System.Reflection; using System.Reflection.Emit; using System; public void AddExecutingFunctionToIL(ILGenerator parentIlGenerator, TypeBuilder typeBuilder, Func<object[], object> function) { MethodInfo originalMethod = function.Method; // 在动态类型中创建匹配原方法签名的静态方法 MethodBuilder wrappedMethod = typeBuilder.DefineMethod( $"Wrapped_{originalMethod.Name}_{Guid.NewGuid():N}", MethodAttributes.Public | MethodAttributes.Static, originalMethod.ReturnType, originalMethod.GetParameters().Select(p => p.ParameterType).ToArray()); ILGenerator methodIl = wrappedMethod.GetILGenerator(); var originalInstructions = originalMethod.GetInstructions(); // 逐行复制原方法的IL指令,处理不同类型的操作数 foreach (var instr in originalInstructions) { switch (instr.OpCode.OperandType) { case OperandType.InlineMethod: methodIl.Emit(instr.OpCode, instr.Operand as MethodInfo); break; case OperandType.InlineField: methodIl.Emit(instr.OpCode, instr.Operand as FieldInfo); break; case OperandType.InlineType: methodIl.Emit(instr.OpCode, instr.Operand as Type); break; case OperandType.InlineString: methodIl.Emit(instr.OpCode, instr.Operand as string); break; case OperandType.InlineI: methodIl.Emit(instr.OpCode, (int)instr.Operand); break; case OperandType.InlineI8: methodIl.Emit(instr.OpCode, (long)instr.Operand); break; case OperandType.InlineR: methodIl.Emit(instr.OpCode, (double)instr.Operand); break; case OperandType.InlineNone: methodIl.Emit(instr.OpCode); break; // 其他操作数类型可根据实际需求补充 default: throw new NotSupportedException($"不支持的操作数类型: {instr.OpCode.OperandType}"); } } // 在父IL生成器中调用这个动态生成的包装方法 parentIlGenerator.Emit(OpCodes.Call, wrappedMethod); }
3. 关键注意事项
- 闭包处理:如果原委托是捕获了外部变量的Lambda表达式,直接复制IL会失败(闭包字段属于原类型,动态类型无法访问)。此时需要:
- 将原闭包类型的字段复制到动态
TypeBuilder中; - 修改IL指令中的字段引用,指向新生成的字段;
- 若原方法是实例方法,需将实例对象作为参数传入动态方法。
- 将原闭包类型的字段复制到动态
- 程序集依赖配置:创建动态程序集时,确保使用正确的
AssemblyBuilder配置,避免System.Private.CoreLib找不到的问题:var assemblyName = new AssemblyName("MyDynamicAssembly"); var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, $"{assemblyName.Name}.dll"); var typeBuilder = moduleBuilder.DefineType("MyDynamicType", TypeAttributes.Public);
内容的提问来源于stack exchange,提问作者Noraa Junker-Wildi
相关产品推荐
相关产品推荐

