DynamicMethod如何传入结构体参数调用泛型SetComponent方法
实现修正与完整代码
现有代码的核心遗漏
- 引用的
genericMethod是泛型方法定义,未绑定ComponentExample的具体泛型参数,无法直接调用 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
相关产品推荐
相关产品推荐

