发出工厂方法

问题描述

给出以下代码

    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生成代码在本文的结尾。

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

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

我认为类定义/ 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)

ILDASM为TestBar1Factory生成了此代码

类别定义

.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);

返回类型必须为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);