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

DynamicMethod如何传入结构体参数调用泛型SetComponent方法

实现修正与完整代码

现有代码的核心遗漏

  1. 引用的genericMethod是泛型方法定义,未绑定ComponentExample的具体泛型参数,无法直接调用
  2. SetComponent是EntityManager结构体的实例方法,调用时必须传入实例的托管指针,当前DynamicMethod参数列表遗漏EntityManager入参,也未处理值类型实例方法的栈加载规则

完整实现流程

1. 构造可调用的具体泛型方法

先从EntityManager类型中获取SetComponent的泛型定义,绑定到ComponentExample的具体类型:

// 获取实例泛型方法的定义
var setComponentDef = typeof(EntityManager).GetMethod(
    nameof(EntityManager.SetComponent),
    BindingFlags.Public | BindingFlags.Instance
);
// 绑定泛型参数,生成可直接调用的具体方法实例
var concreteSetComp = setComponentDef.MakeGenericMethod(typeof(ComponentExample));

2. 修正DynamicMethod参数列表

因为要调用实例方法,DynamicMethod需要先接收EntityManager实例作为第一个参数,后续按顺序跟上Entity、ComponentExample入参:

var dm = new DynamicMethod(
    "SetComponent",       
    returnType: typeof(void),  
    // 参数顺序:EntityManager实例、Entity、ComponentExample
    parameterTypes: new [] { typeof(EntityManager), typeof(Entity), typeof(ComponentExample)}, 
    restrictedSkipVisibility: false
);

3. IL栈参数按顺序加载(核心emit逻辑)

IL栈为后进先出结构,实例方法的栈入栈顺序严格匹配调用约定:

  • 先加载值类型实例的托管指针(值类型实例方法要求this按地址传递,不能直接传值)
  • 再按方法定义的顺序加载显式参数
    对应实现代码:
var il = dm.GetILGenerator();
// 加载第0个参数(EntityManager实例)的托管地址,值类型实例方法必须用Ldarga
il.Emit(OpCodes.Ldarga, 0);
// 加载第1个参数(Entity结构体,按值传递直接用Ldarg即可)
il.Emit(OpCodes.Ldarg, 1);
// 加载第2个参数(ComponentExample结构体,按值传递直接用Ldarg即可)
il.Emit(OpCodes.Ldarg, 2);
// 调用构造好的具体泛型方法
il.EmitCall(OpCodes.Call, concreteSetComp, null);
il.Emit(OpCodes.Ret);

注意事项
  • 结构体作为实例方法的this时,必须用Ldarga加载托管指针,直接按值加载会触发栈不平衡的InvalidProgramException
  • 泛型方法必须先通过MakeGenericMethod绑定具体类型后再emit调用,直接传入泛型定义会触发运行时错误
  • 值类型作为普通入参时,直接用Ldarg按值加载即可,不需要额外处理
  • 生成完成后可通过dm.CreateDelegate(typeof(Action<EntityManager, Entity, ComponentExample>))生成强类型委托,调用性能和原生方法几乎无差异
  • 值类型无虚方法场景下用OpCodes.Call比Callvirt效率更高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 08:36:03