发出工厂方法

Emit a factory method

给定以下代码:

    public interface IBar1 { }
    public class Bar1 : IBar1 { }
    public interface IBar1Factory { IBar1 Factory(); }

我想动态发出一个看起来像这样的类型。

    public class TestBar1Factory : IBar1Factory
    {
        public IBar1 Factory() { return new Bar1(); }
    }

问题

我一直在关注 tutorial here 并且我还创建了类型,手动构建它并在 ILDASM 中查看以发出与已编译 DLL 中完全一样的代码。我试图模仿的 ILDASM 生成的代码位于此 post.

的末尾

我什至无法创建类型,出现此错误:

System.TypeLoadException : Signature of the body and declaration in a method implementation do not match.

我假设 class 定义/ctor/工厂方法中的枚举有问题?但我正在尝试各种组合,问题不会消失:(

我的代码

    AssemblyName asmName = new AssemblyName { Name = "ToFactoryDynamicAssembly" };
    AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
    ModuleBuilder moduleBuilder = asmBuilder.DefineDynamicModule("ToFactoryModule");
    ConstructorInfo systemObjectCtor = Type.GetType("System.Object").GetConstructor(new Type[0]);

    TypeBuilder facBuilder = moduleBuilder.DefineType($"DynamicFactoryFor{typeof(IBar1Factory).Name}", 
        TypeAttributes.Public 
        | TypeAttributes.AutoClass
        | TypeAttributes.AnsiClass
        | TypeAttributes.BeforeFieldInit);

    facBuilder.AddInterfaceImplementation(typeof(IBar1Factory));
            
    ConstructorBuilder facCtorBuilder = facBuilder.DefineConstructor(MethodAttributes.Public
        | MethodAttributes.HideBySig
        | MethodAttributes.SpecialName
        | MethodAttributes.RTSpecialName, CallingConventions.Standard, null);

    var ctorIL = facCtorBuilder.GetILGenerator();
    ctorIL.Emit(OpCodes.Ldarg_0);
    ctorIL.Emit(OpCodes.Call, systemObjectCtor);
    ctorIL.Emit(OpCodes.Nop);
    ctorIL.Emit(OpCodes.Ret);

    var methodBuilder = facBuilder.DefineMethod("Factory", MethodAttributes.Public
        | MethodAttributes.HideBySig
        | MethodAttributes.NewSlot
        | MethodAttributes.Virtual
        | MethodAttributes.Final, typeof(IBar), null);
            
    var bar1Ctor = typeof(Bar1).GetConstructors()[0];
    var methodIL = methodBuilder.GetILGenerator();
    methodIL.Emit(OpCodes.Nop);
    methodIL.Emit(OpCodes.Newobj, bar1Ctor);
    methodIL.Emit(OpCodes.Stloc_0);
    methodIL.Emit(OpCodes.Ldloc_0);
    methodIL.Emit(OpCodes.Ret);

    facBuilder.DefineMethodOverride(methodBuilder, typeof(IBar1Factory).GetMethod("Factory"));
    var type = facBuilder.CreateType(); // error
            
    var ctor = type.GetConstructors()[0];
    IBar1Factory factory = ctor.Invoke(null) as IBar1Factory;
    IBar1 bar1 = factory.Factory();

来自 ILDASM 的工作 IL 代码(来自从 Visual Studio 编译的 DLL)

对于 TestBar1Factory,ILDASM 生成了这个。

CLASS 定义

.class public auto ansi beforefieldinit AddFactoryExtension.Tests.TestBar1Factory
       extends [System.Runtime]System.Object
       implements AddFactoryExtension.Tests.IBar1Factory
{
} // end of class AddFactoryExtension.Tests.TestBar1Factory

CTOR

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       8 (0x8)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
  IL_0006:  nop
  IL_0007:  ret
} // end of method TestBar1Factory::.ctor

工厂方法

.method public hidebysig newslot virtual final 
        instance class AddFactoryExtension.Tests.IBar1 
        Factory() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init (class AddFactoryExtension.Tests.IBar1 V_0)
  IL_0000:  nop
  IL_0001:  newobj     instance void AddFactoryExtension.Tests.Bar1::.ctor()
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method TestBar1Factory::Factory

我在您的代码中看到以下错误。修复这些问题后,我可以 运行 毫无问题地解决您的问题。

var methodBuilder = facBuilder.DefineMethod("Factory", ..., typeof(IBar), null);

return 类型必须是 typeof(IBar1)。传递 Type.EmptyTypes 而不是 null.

也是一个好习惯

在下面的代码中,你有 Stloc 但它需要一个目标局部变量。

var methodIL = methodBuilder.GetILGenerator();
methodIL.Emit(OpCodes.Nop);
methodIL.Emit(OpCodes.Newobj, bar1Ctor);
methodIL.Emit(OpCodes.Stloc_0);
methodIL.Emit(OpCodes.Ldloc_0);

这是一个快速修复

var bar1Ctor = typeof(Bar1).GetConstructors()[0];
var methodIL = methodBuilder.GetILGenerator();
methodIL.Emit(OpCodes.Newobj, bar1Ctor);
methodIL.Emit(OpCodes.Ret);