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 assembly dinâmico representado por um AssemblyBuilder pode ser usado para executar o código emitido.
AssemblyBuilderAccess.RunAndCollect
O assembly dinâmico representado por um AssemblyBuilder pode ser usado para executar o código emitido e é recuperado automaticamente pelo coletor de lixo.
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.