3

If I create a DynamicMethod from inside a class method how can I call another method of my class from the DynamicMethod Delegate? I need somehow to capture the this reference in the DynamicMethod code. But I can't find an overladed version of ILGenerator.Emit which takes an object as parameter.

The code is so far:

    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);
    }

My problem is the line marked with ???

AndiR
  • 179
  • 10
  • 1
    How can *code* have a reference to an object? it can't. You need to pass it as a parameter (at runtime). – Yacoub Massad Dec 15 '15 at 21:15
  • Ok, I have a Communication Channel to a remote System which handles Method calls by 2 arrays of object; one for in and one for out values and the return value. For better type safety I want to provide delegates with the right signature. Therefore I'd like create dynamic delegates for diffrent method signatures. The delegate code will than create the 2 object[] arrays, puts the arguments in the one array, call the internal method and copies the return and out parameters out of the other array and returns. – AndiR Dec 15 '15 at 21:22
  • Since we don't know the issue behind the scenes you're trying to fix or provide a solution I'll just add here a comment: who knows if using expression trees the same issue could be solved without using reflection emit directly.. – Matías Fidemraizer Dec 15 '15 at 21:40
  • Have added the complete code now. – AndiR Dec 15 '15 at 21:52
  • Can't you use expression trees? They support this right away. – usr Dec 15 '15 at 23:07

1 Answers1

4

How to reference the this pointer from a DynamicMethod?

At low level, methods do not have a specific this pointer, instead the first argument of the method is used as this.

To reference the this pointer, here is what you have to do:

  • Prepend a new argument to your argument list, matching the type you want to use as this
  • Use that argument as this wherever you want
  • Create the delegate using DynamicMethod.CreateDelegate(Type,Object) to bind the first parameter to your object: return (D)(object)dm.CreateDelegate(dType, this);

Note that creating dynamic methods is expensive. If you generate the same DynamicMethod for multiple instances, you should cache the method itself, and create delegates using the cached method, but with different target parameter.

Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97