Search code examples
c#genericsnestedreflection.emit

Generate code for generic nested class with Reflection.Emit


I'm trying to generate code using Reflection.Emit which would look identical or similar to what C# compiler generates for this:

public interface Function<in T, out Res>
{
    Res Apply(T p);
}


public class MyMain<A>
{
    class Closure : Function<A, A>
    {
        public A Apply(A p)
        {
            throw new NotImplementedException();
        }
    }
}

My code works fine if I use some real type, but when I replace it with generic, I get BadImageFormatException.

    private static void CreateGenericClosures()
    {
        AssemblyName assemblyName = new AssemblyName("genericClosure");
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");

        TypeBuilder main = moduleBuilder.DefineType("Main", TypeAttributes.Class | TypeAttributes.Public);

        Type t = typeof (Foo);

        // Defining generic param for Main class
        GenericTypeParameterBuilder[] generics = main.DefineGenericParameters("A");
        // t = generics[0]; // [1] Uncomment to enable for nested class

        var iFunctionType = typeof (Function<,>).MakeGenericType(t, t);

        TypeBuilder closure = main.DefineNestedType("Closure", TypeAttributes.Class | TypeAttributes.NestedPrivate, typeof (object));
        closure.AddInterfaceImplementation(iFunctionType);

        MethodBuilder applyMethod = closure.DefineMethod("Apply",
            MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.Public,
            CallingConventions.Standard);
        applyMethod.SetParameters(t);
        applyMethod.SetReturnType(t);

        ILGenerator body = applyMethod.GetILGenerator();
        body.Emit(OpCodes.Ldnull);
        body.Emit(OpCodes.Ret);

        closure.DefineDefaultConstructor(MethodAttributes.Public);

        closure.CreateType();
        main.CreateType();

        assemblyBuilder.Save(assemblyName.Name + ".dll", PortableExecutableKinds.Required32Bit, ImageFileMachine.I386);
    }

Uncomment [1] to see the exception. Any ideas what could be wrong?


Solution

  • It turns out that nested classes don't actually have access to their parents' generic type parameters/arguments and to be well-formed they actually need to redeclare all of the generic type parameters of their parents (see e.g. this old blog post, or section I.10.7.1 of the ECMA CLI spec, which goes into lots of detail about the requirements for CLS compatibility). So what you need to do is something like this:

    ...
    var t = closure.DefineGenericParameters("A")[0];
    var iFunctionType = typeof(Function<,>).MakeGenericType(t, t);
    closure.AddInterfaceImplementation(iFunctionType);
    ...
    

    You've still got some other problems (e.g. your IL for the apply method isn't valid), but this should at least unblock you enough to use PEVerify.