动态方法调用实例方法

3
如果我在类方法中创建一个 DynamicMethod,如何从 DynamicMethod 委托调用我的类的另一个方法?我需要在 DynamicMethod 代码中捕获 this 引用。但是我找不到一个接受对象作为参数的 ILGenerator.Emit 的重载版本。
目前的代码如下:
    void CallOpc(string name, object[] inp, object[] outp)
    {
        //...
    }

    public D CreateDelegate<D>(string opcName) where D : class
    {
        var dType = typeof(D);
        var invoke = dType.GetMethod("Invoke");

        var parameters = invoke.GetParameters();
        var paramTypes = parameters.Select(p => p.ParameterType).ToArray();
        DynamicMethod dm = new DynamicMethod(
            opcName,
            invoke.ReturnType,
            paramTypes,
            true);

        var inp = parameters.Where(p => !p.IsOut).Select(p => p.ParameterType).ToList();
        var outp = parameters.Where(p => p.IsOut).Select(p => p.ParameterType).ToList();
        if (invoke.ReturnType != typeof(void))
        {
            outp.Insert(0, invoke.ReturnType);
        }
        ILGenerator il = dm.GetILGenerator();

        LocalBuilder invar = il.DeclareLocal(typeof(object[]));
        LocalBuilder outvar = il.DeclareLocal(typeof(object[]));

        il.Emit(OpCodes.Ldc_I4, inp.Count);
        il.Emit(OpCodes.Newarr, typeof(object));
        il.Emit(OpCodes.Stloc, invar);

        for (int i = 0; i < inp.Count; i++)
        {
            il.Emit(OpCodes.Ldloc, invar);
            il.Emit(OpCodes.Ldc_I4, i);
            int j = Array.IndexOf(paramTypes, inp[i]);
            il.Emit(OpCodes.Ldarg, j);
            if (!inp[i].IsClass)
            {
                il.Emit(OpCodes.Box, inp[i]);
            }
            il.Emit(OpCodes.Stelem_Ref);
        }

        il.Emit(OpCodes.Ldc_I4, outp.Count);
        il.Emit(OpCodes.Newarr, typeof(object));
        il.Emit(OpCodes.Stloc, outvar);

        il.Emit(OpCodes.Ldarg_0);   // <- push this on the evaluation stack ???
        il.Emit(OpCodes.Ldstr, opcName);
        il.Emit(OpCodes.Ldloc, invar);
        il.Emit(OpCodes.Ldloc, outvar);
        MethodInfo callOpcMeth = GetType().GetMethod("CallOpc", BindingFlags.Instance | BindingFlags.NonPublic);
        il.Emit(OpCodes.Callvirt, callOpcMeth);


        for (int o = 0; o < outp.Count; o++)
        {
            // TODO: handle out params and return value
        }
        il.Emit(OpCodes.Ret);
        return (D)(object)dm.CreateDelegate(dType);
    }

我的问题出现在标记为 ??? 的那一行。


1
代码如何引用一个对象?它不可能。你需要在运行时将其作为参数传递。 - Yacoub Massad
好的,我有一个与远程系统通信的通道,该系统通过两个对象数组处理方法调用;一个用于输入和一个用于输出值和返回值。为了更好地保证类型安全性,我想提供具有正确签名的委托。因此,我想为不同的方法签名创建动态委托。委托代码将创建2个object[]数组,将参数放入一个数组中,调用内部方法,并将返回值和输出参数从另一个数组中复制并返回。 - AndiR
由于我们不知道你正在尝试修复或提供解决方案的背后问题,我在这里只会添加一条评论:谁知道如果使用表达式树,是否可以直接解决相同的问题而无需使用反射发射。 - Matías Fidemraizer
现在已添加完整的代码。 - AndiR
你不能使用表达式树吗?它们可以立即支持这个。 - usr
1个回答

4
如何从DynamicMethod中引用this指针?
在低层次上,方法没有特定的this指针,而是使用方法的第一个参数作为this
要引用this指针,您需要执行以下操作:
  • 在参数列表中添加一个新参数,其类型匹配您要用作this的类型
  • 在您想要的任何位置使用该参数作为this
  • 使用DynamicMethod.CreateDelegate(Type,Object)创建委托,将第一个参数绑定到您的对象:return (D)(object)dm.CreateDelegate(dType, this);
请注意,创建动态方法很昂贵。如果您为多个实例生成相同的DynamicMethod,则应缓存方法本身,并使用不同的target参数创建委托。

谢谢,现在它按预期工作。是的,我会缓存已创建的委托。 - AndiR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接