使用 Reflection.Emit 为通用嵌套类生成代码

问题描述 投票:0回答:1

我正在尝试使用 Reflection.Emit 生成代码,它看起来与 C# 编译器为此生成的代码相同或相似:

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

如果我使用一些真实类型,我的代码工作正常,但是当我用泛型替换它时,我得到 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);
    }

取消注释 [1] 以查看异常。 有什么想法可能是错的吗?

c# generics nested reflection.emit
1个回答
1
投票

事实证明,嵌套类实际上无法访问其父级的泛型类型参数/参数,并且为了格式良好,它们实际上需要重新声明其父级的所有泛型类型参数(例如参见 this old blog post ,或ECMA CLI 规范 的 I.10.7.1 节,其中详细介绍了有关 CLS 兼容性要求的详细信息)。所以你需要做的是这样的:

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

您仍然遇到一些其他问题(例如,您的 apply 方法的 IL 无效),但这至少应该可以让您充分使用 PEVerify。

© www.soinside.com 2019 - 2024. All rights reserved.