Compartilhar via


System.Reflection.Emit.AssemblyBuilder class

Este artigo fornece comentários complementares à documentação de referência para esta 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 o 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 somente memória regular é chamado 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 enumeração, 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. Conjuntos dinâmicos podem ser criados utilizando um dos seguintes modos de acesso.

O modo de acesso deve ser especificado fornecendo o valor apropriado AssemblyBuilderAccess na chamada para o método AssemblyBuilder.DefineDynamicAssembly quando o assembly dinâmico é definido e não pode ser alterado posteriormente. O runtime 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 uma montagem:

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

No .NET, o PersistedAssemblyBuilder tipo, do qual deriva AssemblyBuilder, permite salvar assemblies dinâmicos. Para obter mais informações, consulte os cenários de uso e exemplos em PersistedAssemblyBuilder.

Assemblies dinâmicas persistentes no .NET Framework

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

Os módulos dinâmicos no assembly dinâmico armazenável são salvos quando o assembly dinâmico é salvo usando o método Save. Para gerar um executável, o método SetEntryPoint deve ser chamado para identificar o método que é o ponto de entrada da assemblagem. 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 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 sobre 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 ao DefineDynamicModule método.

A assinatura de um assemblage dinâmico usando KeyPair não é eficaz até que o assemblage seja salvo no disco. Portanto, nomes fortes não funcionarão com assemblagens dinâmicas transitórias.

Os assemblies dinâmicos podem fazer referência a tipos definidos em outro assembly. Uma assembleia dinâmica transitória pode referenciar com segurança tipos definidos em outra assembleia dinâmica transitória, uma assembleia dinâmica persistente ou uma assembleia estática. 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 em disco, o runtime não pode resolver as referências aos tipos definidos no módulo dinâmico transitório.

Restrições a transmissão para domínios de aplicação 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 em disco e carregar o assembly dinâmico no domínio do aplicativo remoto. Os domínios de comunicação remota e de aplicativo têm suporte apenas no .NET Framework.