Partilhar via


System.Reflection.Emit.AssemblyBuilder classe

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 salvar é chamado de assembly persistente , enquanto o assembly regular somente de 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, verifique 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 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 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 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 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 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, 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.