如何用System.Reflection.Emit生成委托调用Message.Say?报错原因与解决
问题原因与解决方案
错误原因
- 缺少实例方法所需的
this指针:Message.Say是实例方法,调用时必须先提供Message类的实例(即this指针)作为第一个参数,但你的DynamicMethod仅定义了string类型的参数,无法为Say方法提供必要的实例,导致CLR检测到无效的IL程序。 - IL指令栈操作错误:你使用
OpCodes.Ldarg_1加载第二个参数,但动态方法仅定义了一个参数(string),此时栈上没有合法的Message实例,执行Callvirt时栈状态不匹配方法调用要求,触发InvalidProgramException。 - 不安全的方法获取方式:
typeof(Message).GetMethods()[0]依赖方法的返回顺序,若后续类中新增方法,会导致获取错误的方法,进而引发调用异常。
修复方案
方案1:创建需传入实例的委托
定义动态方法时包含Message实例和string参数,IL指令依次加载实例、参数后调用方法:
public class Message { public SayType Say(string name) { Console.Write("Hello," + name); return SayType.Name; } } public enum SayType { Name } // 生成动态方法与委托 DynamicMethod dynamicMethod = new DynamicMethod("InvokeSay", typeof(SayType), new Type[] { typeof(Message), typeof(string) }); ILGenerator il = dynamicMethod.GetILGenerator(); // 加载Message实例(this指针) il.Emit(OpCodes.Ldarg_0); // 加载string类型的name参数 il.Emit(OpCodes.Ldarg_1); // 明确获取Say方法,避免依赖顺序 MethodInfo sayMethod = typeof(Message).GetMethod("Say", new Type[] { typeof(string) }); il.Emit(OpCodes.Callvirt, sayMethod); // 返回方法结果 il.Emit(OpCodes.Ret); // 创建委托并调用 Func<Message, string, SayType> sayDelegate = (Func<Message, string, SayType>)dynamicMethod.CreateDelegate(typeof(Func<Message, string, SayType>)); Message msg = new Message(); SayType result = sayDelegate(msg, "e");
方案2:绑定到特定实例的委托
若想让委托无需每次传入Message实例,可将动态方法关联到Message类型,创建委托时绑定到特定实例:
// 定义动态方法:参数仅为string,owner指定为Message类型 DynamicMethod dynamicMethod = new DynamicMethod("InvokeSayBound", typeof(SayType), new Type[] { typeof(string) }, typeof(Message)); ILGenerator il = dynamicMethod.GetILGenerator(); // 加载this指针(关联Message类型后,Ldarg_0为Message实例) il.Emit(OpCodes.Ldarg_0); // 加载string参数 il.Emit(OpCodes.Ldarg_1); MethodInfo sayMethod = typeof(Message).GetMethod("Say", new Type[] { typeof(string) }); il.Emit(OpCodes.Callvirt, sayMethod); il.Emit(OpCodes.Ret); // 创建委托时绑定到指定Message实例 Message msg = new Message(); Func<string, SayType> boundDelegate = (Func<string, SayType>)dynamicMethod.CreateDelegate(typeof(Func<string, SayType>), msg); // 直接传入string参数调用 SayType result = boundDelegate("ha");
关键注意点
- 调用实例方法必须保证栈上依次压入
this指针和方法的所有参数,IL指令的顺序必须严格匹配。 - 获取方法时优先使用
GetMethod(string name, Type[] parameterTypes)明确指定方法名和参数类型,避免依赖方法列表顺序。 - 若需绑定到特定实例,可通过
DynamicMethod的构造函数指定owner类型,再使用CreateDelegate的重载传入实例。
内容的提问来源于stack exchange,提问作者DamXosp4j
相关产品推荐
相关产品推荐

