c# - 发出工厂方法
问题描述
给定以下代码:
public interface IBar1 { }
public class Bar1 : IBar1 { }
public interface IBar1Factory { IBar1 Factory(); }
我想动态发出一个看起来像这样的类型。
public class TestBar1Factory : IBar1Factory
{
public IBar1 Factory() { return new Bar1(); }
}
问题
我在这里按照教程进行操作,还创建了类型,手动构建它并在 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)
对于TestBar1Factory
ILDASM 生成这个。
类定义
.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);
推荐阅读
- android - 在 Firebase 控制台项目中添加 Xamarin.Android 应用程序的问题
- javascript - 在 Node.js 中从 SQLite 发送所有数据
- kubernetes - calico 无法 ping 其他使用 calico 网络创建的 POD
- node.js - 帮助我解决 npm 创建第一个项目错误
- reactjs - 在 Mediastream 中切换启用的轨道时,无法关闭浏览器中的绿灯和红色图标
- apache-spark - PySpark 将两个数据帧写入同一个分区但由文件夹分隔
- java - 汇合的 jdbc-source-connector 不能在 Java 中工作
- reactjs - 如何防止Router在没有有效id的情况下访问routed?
- python - Djano 第一个项目搜索问题
- python - 将 dataFrame 值映射到另一个 DataFrame