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

.NET Reflection Emit编写MSIL方法报错,求排查及性能咨询

Reflection Emit 动态委托错误排查及性能分析

一、InvalidProgramException 错误排查

这类异常本质是生成的IL指令不符合CLR执行规范,常见问题及修复方案如下:

常见错误点及修复

  1. 栈操作不平衡
    IL基于栈执行,每一步操作必须保证栈的压入/弹出数量匹配。比如调用List.Add后栈需清空,否则后续指令会触发异常。正确流程需严格遵循「加载对象→获取属性值→装箱(值类型)→加载List→调用Add」,确保栈状态始终合法。

  2. 值类型未装箱
    值类型(如int、DateTime)直接转换为object时必须使用Box指令,否则CLR无法识别类型。遍历属性时需判断PropertyType.IsValueType,若是则插入OpCodes.Box指令完成装箱。

  3. 委托签名不匹配
    动态方法的参数、返回值类型必须与目标委托完全一致。比如要生成Func<T, List<object>>,动态方法的参数必须为T类型,返回值必须是List<object>。

  4. 方法调用指令错误
    属性get方法为虚方法时用Callvirt,非虚方法用Call;Callvirt对非虚方法也能正常执行,但调用静态方法必须用Call。

正确实现示例

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

public static class PropertyExtractorGenerator
{
    public static Func<T, List<object>> CreatePropertyExtractor<T>()
    {
        var targetType = typeof(T);
        var properties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        
        var dynamicMethod = new DynamicMethod(
            $"Extract_{targetType.Name}_Props",
            typeof(List<object>),
            new[] { targetType },
            typeof(PropertyExtractorGenerator).Module,
            skipVisibility: true);
        
        var il = dynamicMethod.GetILGenerator();
        
        // 初始化List<object>并存入局部变量
        il.Emit(OpCodes.Newobj, typeof(List<object>).GetConstructor(Type.EmptyTypes));
        var listLocal = il.DeclareLocal(typeof(List<object>));
        il.Emit(OpCodes.Stloc, listLocal);
        
        foreach (var prop in properties)
        {
            // 加载输入对象(参数0)
            il.Emit(OpCodes.Ldarg_0);
            // 调用属性get方法
            il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
            
            // 值类型装箱
            if (prop.PropertyType.IsValueType)
            {
                il.Emit(OpCodes.Box, prop.PropertyType);
            }
            
            // 加载List实例并调用Add方法
            il.Emit(OpCodes.Ldloc, listLocal);
            il.Emit(OpCodes.Callvirt, typeof(List<object>).GetMethod("Add", new[] { typeof(object) }));
        }
        
        // 返回List实例
        il.Emit(OpCodes.Ldloc, listLocal);
        il.Emit(OpCodes.Ret);
        
        return (Func<T, List<object>>)dynamicMethod.CreateDelegate(typeof(Func<T, List<object>>));
    }
}

// 测试用类
public class DemoClass
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
}

// 使用方式
var extractor = PropertyExtractorGenerator.CreatePropertyExtractor<DemoClass>();
var demo = new DemoClass { Id = 101, Name = "Alice", BirthDate = new DateTime(1990, 1, 1) };
var propValues = extractor(demo);

二、性能问题分析

1. 生成委托的调用性能

与编译后的静态代码几乎完全一致。动态生成的IL会被CLR的JIT编译器编译为原生机器码,后续调用委托时直接执行原生代码,无反射调用的额外开销。

2. 反射获取成员信息的性能

仅在委托生成阶段有一次开销(调用GetProperties获取属性信息),后续调用委托时不会再触发反射操作。若对同一类型多次调用提取器,初始化阶段的开销可忽略;追求极致性能可缓存生成的委托(比如用静态字典存储类型与对应委托的映射)。

内容的提问来源于stack exchange,提问作者user2173353

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:36:06