Search code examples
c#genericsreflection.emittypebuilder

How to call a generic base method with TypeBuilder


I have the following class: (FYI: I can't edit the base class)

public abstract class BaseClass
{
    protected TOutput Convert<TInput, TOutput>(TInput input)
    {
        return (TOutput)System.Convert.ChangeType(input, typeof(TOutput));
    }
}

And what I'm trying to build, is a dynamic type like this:

public class DynamicClass : BaseClass
{
    public string Convert(int value)
    {
        return base.Convert<int, string>(value);
    }
}

The method that I use to build the dynamic type, looks like this:

private MethodBuilder BuildConvertMethod(TypeBuilder typeBuilder)
{
    var baseConvertMethod = typeof(BaseClass).GetMethod("Convert", BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance);
    var baseConvertMethodInstance = baseConvertMethod.MakeGenericMethod(typeof(int), typeof(string));

    var convertMethodBuilder = typeBuilder.DefineMethod("Convert", MethodAttributes.Public, typeof(string), new[] { typeof(int) });

    var convertMethodILGenerator = convertMethodBuilder.GetILGenerator();
    convertMethodILGenerator.Emit(OpCodes.Nop);
    convertMethodILGenerator.Emit(OpCodes.Ldarg_0);
    convertMethodILGenerator.Emit(OpCodes.Ldarg_1);
    convertMethodILGenerator.Emit(OpCodes.Call, baseConvertMethodInstance);
    convertMethodILGenerator.Emit(OpCodes.Ldloc_0);
    convertMethodILGenerator.Emit(OpCodes.Ret);

    return convertMethodBuilder;
}

The creation of the type succeeds. But when I create an instance of DynamicClass, and then calls the Convert method I get the following exception:

System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation. ----> System.InvalidProgramException : Common Language Runtime detected an invalid program.

Can someone please help me with this problem?


Solution

  • Call instructions push the return value on the stack so you can call Ret straight after.

    var convertMethodILGenerator = convertMethodBuilder.GetILGenerator();
    convertMethodILGenerator.Emit(OpCodes.Ldarg_0);
    convertMethodILGenerator.Emit(OpCodes.Ldarg_1);
    convertMethodILGenerator.Emit(OpCodes.Call, baseConvertMethodInstance);
    convertMethodILGenerator.Emit(OpCodes.Ret);