Compartilhar via


Classe System.Reflection.Emit.AssemblyBuilder

Este artigo fornece observações complementares à documentação de referência para essa API.

Um assembly dinâmico é um assembly criado usando as APIs de emissão de reflexão. Um assembly dinâmico pode fazer referência a tipos definidos em outro assembly dinâmico ou estático. Você pode usar AssemblyBuilder para gerar assemblies dinâmicos na memória e executar seu código durante a execução do mesmo aplicativo. No .NET 9, adicionamos um novo PersistedAssemblyBuilder com implementação totalmente gerenciada de emissão de reflexão que permite salvar o assembly em um arquivo. No .NET Framework, você pode fazer as duas coisas — executar o assembly dinâmico e salvá-lo em um arquivo. O assembly dinâmico criado para salvar é chamado de assembly persistente , enquanto o assembly regular somente memória é chamado de transitório ou executável. No .NET Framework, um assembly dinâmico pode consistir em um ou mais módulos dinâmicos. No .NET Core e no .NET 5+, um assembly dinâmico só pode consistir em um módulo dinâmico.

A maneira como você cria uma AssemblyBuilder instância difere para cada implementação, mas as etapas adicionais para definir um módulo, tipo, método ou enum e para escrever IL são bastante semelhantes.

Assemblies dinâmicos executáveis no .NET

Para obter um objeto executável AssemblyBuilder , use o AssemblyBuilder.DefineDynamicAssembly método. Os assemblies dinâmicos podem ser criados usando um dos seguintes modos de acesso:

O modo de acesso deve ser especificado fornecendo o valor apropriado AssemblyBuilderAccess na chamada para o AssemblyBuilder.DefineDynamicAssembly método quando o assembly dinâmico é definido e não pode ser alterado posteriormente. O tempo de execução usa o modo de acesso de um assembly dinâmico para otimizar a representação interna do assembly.

O exemplo a seguir demonstra como criar e executar um assembly:

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

Assemblies dinâmicos persistentes no .NET

O novo PersistedAssemblyBuilder tipo derivado do AssemblyBuilder e permite salvar os assemblies dinâmicos no .NET Core, verificar os cenários de uso e exemplos da página PersistedAssemblyBuilder .

Assemblies dinâmicos persistentes no .NET Framework

No .NET Framework, assemblies dinâmicos e módulos podem ser salvos em arquivos. Para oferecer suporte a esse recurso, a AssemblyBuilderAccess enumeração declara dois campos adicionais: Save e RunAndSave.

Os módulos dinâmicos no assembly dinâmico persistente são salvos quando o assembly dinâmico é salvo usando o Save método. Para gerar um executável, o SetEntryPoint método deve ser chamado para identificar o método que é o ponto de entrada para o assembly. Os assemblies são salvos como DLLs por padrão, a menos que o SetEntryPoint método solicite a geração de um aplicativo de console ou um aplicativo baseado no Windows.

O exemplo a seguir demonstra como criar, salvar e executar um assembly usando o .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");
}

Alguns métodos na classe base Assembly , como GetModules e GetLoadedModules, não funcionarão corretamente quando chamados a partir de AssemblyBuilder objetos. Você pode carregar o assembly dinâmico definido e chamar os métodos no assembly carregado. Por exemplo, para garantir que os módulos de recursos sejam incluídos na lista de módulos retornados, chame GetModules o objeto carregado Assembly . Se um assembly dinâmico contiver mais de um módulo dinâmico, o nome do arquivo de manifesto do assembly deverá corresponder ao nome do módulo especificado como o primeiro argumento do DefineDynamicModule método.

A assinatura de um assembly dinâmico usando KeyPair não é eficaz até que o assembly seja salvo em disco. Assim, nomes fortes não funcionarão com montagens dinâmicas transitórias.

Os assemblies dinâmicos podem fazer referência a tipos definidos em outro assembly. Um assembly dinâmico transitório pode referenciar com segurança tipos definidos em outro assembly dinâmico transitório, um assembly dinâmico persistente ou um assembly estático. No entanto, o Common Language Runtime não permite que um módulo dinâmico persistente faça referência a um tipo definido em um módulo dinâmico transitório. Isso ocorre porque quando o módulo dinâmico persistente é carregado após ser salvo no disco, o tempo de execução não pode resolver as referências a tipos definidos no módulo dinâmico transitório.

Restrições à emissão para domínios de aplicativos remotos

Alguns cenários exigem que um assembly dinâmico seja criado e executado em um domínio de aplicativo remoto. A emissão de reflexão não permite que um assembly dinâmico seja emitido diretamente para um domínio de aplicativo remoto. A solução é emitir o assembly dinâmico no domínio do aplicativo atual, salvar o assembly dinâmico emitido no disco e carregar o assembly dinâmico no domínio do aplicativo remoto. Os domínios de aplicativo e comunicação remota são suportados somente no .NET Framework.