使用.NET EMIT创建多参数动态委托触发InvalidProgramException的原因
问题描述
相关定义
public class Message { public SayType Say(string name) { Console.Write("Hello," + name ); return SayType.Name; } public SayType Say(string name,string haha) { Console.Write("Hello," + name ); return SayType.Name; } } public enum SayType { Name }
SayDelegate 定义
public delegate SayType SayDelegate(object message,params object[] o);
我想要为Message类中的两个方法创建动态委托:
单参数方法的动态委托(可正常运行)
DynamicMethod dynamicMethod = new DynamicMethod("Say",typeof(SayType),new Type[]{typeof(object),typeof(object[])}); var il = dynamicMethod.GetILGenerator(); il.Emit(OpCodes.Ldarg,0); il.Emit(OpCodes.Ldarg,1); il.Emit(OpCodes.Ldc_I4,0); il.Emit(OpCodes.Ldelem_Ref); il.Emit(OpCodes.Callvirt,typeof(Message).GetMethods()[0]); //[0] refers to the first method il.Emit(OpCodes.Ret); System.Delegate delegates = dynamicMethod.CreateDelegate(typeof(SayDelegate)); delegates.DynamicInvoke(new Message(), new object[]{"123123"});
双参数方法的动态委托(抛出异常)
DynamicMethod dynamicMethod = new DynamicMethod("Say",typeof(SayType),new Type[]{typeof(object),typeof(object[])}); var il = dynamicMethod.GetILGenerator(); il.Emit(OpCodes.Ldarg,0); il.Emit(OpCodes.Ldarg,1); il.Emit(OpCodes.Ldc_I4,0); il.Emit(OpCodes.Ldelem_Ref); il.Emit(OpCodes.Ldc_I4,1); il.Emit(OpCodes.Ldelem_Ref); il.Emit(OpCodes.Callvirt,typeof(Message).GetMethods()[1]); //[1] refers to the second method il.Emit(OpCodes.Ret); System.Delegate delegates = dynamicMethod.CreateDelegate(typeof(SayDelegate)); delegates.DynamicInvoke(new Message(), new object[]{"123123","123123"});
异常信息
---> System.InvalidProgramException: Common Language Runtime detected an invalid program. at Say(Object , Object[] ) --- End of inner exception stack trace ---
请问为何会出现此问题?
问题分析与解决
问题根源
异常是因为你生成的IL指令栈顺序错误,不符合CLR方法调用的要求。
调用实例方法时,栈的正确顺序应该是:[实例对象] → [方法参数1] → [方法参数2]。但你的代码在压入实例对象后,先把整个参数数组(Ldarg,1)压到了栈上,后续取出两个参数后,栈的实际顺序变成了:[实例对象] → [参数数组] → [参数1] → [参数2]——多余的参数数组占据了栈位置,导致CLR无法识别正确的方法调用签名,从而抛出InvalidProgramException。
修正后的代码
需要移除多余的数组压栈操作,每次取参数时单独加载数组:
DynamicMethod dynamicMethod = new DynamicMethod("Say", typeof(SayType), new Type[] { typeof(object), typeof(object[]) }); var il = dynamicMethod.GetILGenerator(); // 压入实例对象 il.Emit(OpCodes.Ldarg, 0); // 取出第一个参数 il.Emit(OpCodes.Ldarg, 1); il.Emit(OpCodes.Ldc_I4, 0); il.Emit(OpCodes.Ldelem_Ref); // 取出第二个参数 il.Emit(OpCodes.Ldarg, 1); il.Emit(OpCodes.Ldc_I4, 1); il.Emit(OpCodes.Ldelem_Ref); // 调用双参数方法 il.Emit(OpCodes.Callvirt, typeof(Message).GetMethods()[1]); il.Emit(OpCodes.Ret); System.Delegate delegates = dynamicMethod.CreateDelegate(typeof(SayDelegate)); delegates.DynamicInvoke(new Message(), new object[] { "123123", "123123" });
额外建议
typeof(Message).GetMethods()返回的方法顺序不固定,建议通过方法名和参数类型精准定位目标方法,避免后续因方法顺序变化出错:
var targetMethod = typeof(Message).GetMethod("Say", new[] { typeof(string), typeof(string) }); // 后续用targetMethod代替typeof(Message).GetMethods()[1]
内容的提问来源于stack exchange,提问作者DamXosp4j
相关产品推荐
相关产品推荐

