You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用System.Reflection.Emit生成委托调用Message.Say?报错原因与解决

问题原因与解决方案

错误原因

  1. 缺少实例方法所需的this指针:Message.Say是实例方法,调用时必须先提供Message类的实例(即this指针)作为第一个参数,但你的DynamicMethod仅定义了string类型的参数,无法为Say方法提供必要的实例,导致CLR检测到无效的IL程序。
  2. IL指令栈操作错误:你使用OpCodes.Ldarg_1加载第二个参数,但动态方法仅定义了一个参数(string),此时栈上没有合法的Message实例,执行Callvirt时栈状态不匹配方法调用要求,触发InvalidProgramException。
  3. 不安全的方法获取方式: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 22:45:25