System.Reflection.Emit.AssemblyBuilder 类

本文提供了此 API 参考文档的补充说明。

动态程序集是使用反应发出 API 创建的程序集。 动态程序集可以引用在另一个动态或静态程序集中定义的类型。 可用于 AssemblyBuilder 在内存中生成动态程序集,并在相同的应用程序运行期间执行其代码。 在 .NET 9 中,我们添加了一个新的 PersistedAssemblyBuilder ,其中包含反射发出完全托管的实现,允许将程序集保存到文件中。 在 .NET Framework 中,可以同时运行动态程序集并将其保存到文件中。 为保存而创建的动态程序集称为 持久 程序集,而常规的仅内存程序集称为 暂时 性程序集或 运行程序集。 在 .NET Framework 中,动态程序集可以包含一个或多个动态模块。 在 .NET Core 和 .NET 5+ 中,动态程序集只能包含一个动态模块。

为每个实现创建 AssemblyBuilder 实例的方式有所不同,但定义模块、类型、方法或枚举以及编写 IL 的进一步步骤非常相似。

.NET 中的可运行动态程序集

若要获取可 AssemblyBuilder 运行的对象,请使用 AssemblyBuilder.DefineDynamicAssembly 该方法。 可以使用以下访问模式之一创建动态程序集:

必须通过在定义动态程序集时在调用AssemblyBuilder.DefineDynamicAssembly方法中提供适当的AssemblyBuilderAccess值来指定访问模式,并且以后不能更改。 运行时使用动态程序集的访问模式来优化程序集的内部表示形式。

以下示例演示如何创建和运行程序集:

public void CreateAndRunAssembly(string assemblyPath)
{
    AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("MyAssembly"), AssemblyBuilderAccess.Run);
    ModuleBuilder mob = ab.DefineDynamicModule("MyModule");
    TypeBuilder tb = mob.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
    MethodBuilder mb = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static,
                                                                   typeof(int), new Type[] {typeof(int), typeof(int)});
    ILGenerator il = mb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Ret);

    Type type = tb.CreateType();

    MethodInfo method = type.GetMethod("SumMethod");
    Console.WriteLine(method.Invoke(null, new object[] { 5, 10 }));
}

.NET 中的持久动态程序集

派生自 AssemblyBuilder .NET Core 中的动态程序集的新PersistedAssemblyBuilder类型,检查 PersistedAssemblyBuilder 页中的使用方案和示例

.NET Framework 中的持久动态程序集

在 .NET Framework 中,动态程序集和模块可以保存到文件中。 为了支持此功能, AssemblyBuilderAccess 枚举声明两个附加字段: SaveRunAndSave

使用 Save 该方法保存动态程序集时,将保存持久动态程序集中的动态模块。 若要生成可执行文件, SetEntryPoint 必须调用该方法来标识作为程序集入口点的方法。 默认情况下,程序集将保存为 DLL,除非 SetEntryPoint 该方法请求生成控制台应用程序或基于 Windows 的应用程序。

以下示例演示如何使用 .NET Framework 创建、保存和运行程序集。

public void CreateRunAndSaveAssembly(string assemblyPath)
{
    AssemblyBuilder ab = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName("MyAssembly"), AssemblyBuilderAccess.RunAndSave);
    ModuleBuilder mob = ab.DefineDynamicModule("MyAssembly.dll");
    TypeBuilder tb = mob.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
    MethodBuilder meb = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static,
                                                                   typeof(int), new Type[] {typeof(int), typeof(int)});
    ILGenerator il = meb.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Ret);

    Type type = tb.CreateType();

    MethodInfo method = type.GetMethod("SumMethod");
    Console.WriteLine(method.Invoke(null, new object[] { 5, 10 }));
    ab.Save("MyAssembly.dll");
}

Assembly 类上的某些方法(例如 GetModules ,从 GetLoadedModules对象调用 AssemblyBuilder 时)无法正常工作。 可以加载定义的动态程序集,并在加载的程序集上调用方法。 例如,为了确保资源模块包含在返回的模块列表中,请调用 GetModules 加载 Assembly 的对象。 如果动态程序集包含多个动态模块,则程序集的清单文件名应与指定为该方法的第一个参数 DefineDynamicModule 的模块名称匹配。

在将程序集保存到磁盘之前,动态程序集 KeyPair 的签名才有效。 因此,强名称不适用于暂时性动态程序集。

动态程序集可以引用在另一个程序集中定义的类型。 暂时性动态程序集可以安全地引用在另一个暂时性动态程序集、持久动态程序集或静态程序集中定义的类型。 但是,公共语言运行时不允许持久动态模块引用在暂时性动态模块中定义的类型。 这是因为在保存到磁盘后加载持久动态模块时,运行时无法解析对在暂时性动态模块中定义的类型的引用。

对向远程应用程序域发出限制

某些方案要求在远程应用程序域中创建和执行动态程序集。 反应ion 发出不允许将动态程序集直接发送到远程应用程序域。 解决方案是在当前应用程序域中发出动态程序集,将发出的动态程序集保存到磁盘,然后将动态程序集加载到远程应用程序域中。 远程处理和应用程序域仅在 .NET Framework 中受支持。