Why can I not bind a DynamicMethod to a struct instance?

276 Views Asked by At

DynamicMethods allow you to specify a target instance for the delegate you create. However, it appears that this does not work when you use a struct type. It fails with an exception telling me it cannot bind to this method. Is the error because my IL does not unbox the target instance?

If I change A here to a class it works without issue. What am I doing wrong? (Also please do not suggest calling Delegate.CreateDelegate to bind to the GetType method with a target instance)

Here is a sample repro:

struct A { }
... //then some where in code::
Func<Type> f = CodeGen.CreateDelegate<Func<Type>>(il=>
    il.ldarga_s(0)
    .constrained(typeof(A))
    .callvirt(typeof(object).GetMethod("GetType"))
    .ret(),
    name:"Constrained",
    target:new A()
);

Note: I'm using the Emitted library for the fluent interface for IL. Also Here is the code for the CodeGen Method.

public static class CodeGen
{
    public static TDelegate CreateDelegate<TDelegate>(Action<ILGenerator> genFunc, string name = "", object target = null, bool restrictedSkipVisibility = false)
        where TDelegate:class
    {
        ArgumentValidator.AssertGenericIsDelegateType(() => typeof(TDelegate));
        ArgumentValidator.AssertIsNotNull(() => genFunc);

        var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
        var @params = invokeMethod.GetParameters();
        var paramTypes = new Type[@params.Length + 1];
        paramTypes[0] = target == null ? typeof(object) : target.GetType();
        @params.ConvertAll(p => p.ParameterType)
            .CopyTo(paramTypes, 1);
        var method = new DynamicMethod(name ?? string.Empty, invokeMethod.ReturnType, paramTypes, restrictedSkipVisibility);
        genFunc(method.GetILGenerator());

        return method.CreateDelegate<TDelegate>(target);
    }
}
1

There are 1 best solutions below

0
On

See the important note at http://msdn.microsoft.com/en-us/library/74x8f551.aspx, which also applies here:

If method is static (Shared in Visual Basic) and its first parameter is of type Object or ValueType, then firstArgument can be a value type. In this case firstArgument is automatically boxed. Automatic boxing does not occur for any other arguments, as it would in a C# or Visual Basic function call.

The implication is that the first argument to your dynamic method will need to be of type object, and you'll need to do a ldarg_0 followed by an unbox before doing the constrained call.