Partilhar via


System.Reflection.Emit.AssemblyBuilder class

Este artigo fornece observações 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 seu código durante a mesma execução do 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 guardar é chamado de assembly persisted, enquanto o assembly regular apenas em memória é conhecido como assembly transient ou runnable. 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 assemblagens dinâmicas podem ser criadas 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 runtime utiliza 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

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

Assemblies dinâmicos persistentes no .NET Framework

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

Os módulos dinâmicos na assembleia dinâmica persistente são salvos quando a assembleia dinâmica é salva usando o método Save. Para gerar um executável, o método SetEntryPoint deve ser chamado para identificar qual é o método que serve como ponto de entrada na 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. É possível carregar o assembly dinâmico definido e invocar os métodos no assembly carregado. Por exemplo, para garantir que os módulos de recursos estejam incluídos na lista de módulos retornados, invoque GetModules no 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 para o DefineDynamicModule método.

A assinatura de um assembly dinâmico usando KeyPair não é efetiva até que o assembly seja salvo no disco. Assim, nomes fortes não funcionarão com assemblies dinâmicos transitórios.

Os assemblies dinâmicos podem fazer referência a tipos definidos em outro assembly. Um conjunto dinâmico transitório pode referenciar com segurança tipos definidos em outro conjunto dinâmico transitório, um conjunto dinâmico persistente ou um conjunto 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 depois de ser salvo no disco, o tempo de execução não pode resolver as referências aos tipos definidos no módulo dinâmico transitório.

Restrições na 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 aplicação remoto. A solução é emitir o assembly dinâmico no domínio do aplicativo atual, salvar o assembly dinâmico emitido no disco e, em seguida, carregar o assembly dinâmico no domínio do aplicativo remoto. Os domínios de comunicação remota e de aplicativo são suportados apenas no .NET Framework.