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

使用.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 23:11:14