Classe System.Reflection.Emit.AssemblyBuilder

Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.

Un assembly dinamico è un assembly creato usando le API Reflection Emit. Un assembly dinamico può fare riferimento a tipi definiti in un altro assembly dinamico o statico. È possibile usare AssemblyBuilder per generare assembly dinamici in memoria ed eseguirne il codice durante la stessa esecuzione dell'applicazione. In .NET 9 è stato aggiunto un nuovo PersistedAssemblyBuilder con implementazione completamente gestita di reflection emit che consente di salvare l'assembly in un file. In .NET Framework è possibile eseguire entrambe le operazioni, eseguire l'assembly dinamico e salvarlo in un file. L'assembly dinamico creato per il salvataggio viene chiamato assembly persistente, mentre l'assembly regolare di sola memoria viene chiamato temporaneo o eseguibile. In .NET Framework un assembly dinamico può essere costituito da uno o più moduli dinamici. In .NET Core e .NET 5+, un assembly dinamico può essere costituito solo da un modulo dinamico.

Il modo in cui si crea un'istanza AssemblyBuilder è diversa per ogni implementazione, ma altri passaggi per la definizione di un modulo, un tipo, un metodo o un'enumerazione e per la scrittura di IL sono molto simili.

Assembly dinamici eseguibili in .NET

Per ottenere un oggetto eseguibile AssemblyBuilder , utilizzare il AssemblyBuilder.DefineDynamicAssembly metodo . È possibile creare assembly dinamici usando una delle modalità di accesso seguenti:

La modalità di accesso deve essere specificata specificando il valore appropriato AssemblyBuilderAccess nella chiamata al AssemblyBuilder.DefineDynamicAssembly metodo quando viene definito l'assembly dinamico e non può essere modificato in un secondo momento. Il runtime usa la modalità di accesso di un assembly dinamico per ottimizzare la rappresentazione interna dell'assembly.

Nell'esempio seguente viene illustrato come creare ed eseguire un 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 }));
}

Assembly dinamici persistenti in .NET

Il nuovo PersistedAssemblyBuilder tipo derivato da AssemblyBuilder e consente di salvare gli assembly dinamici in .NET Core, controllare gli scenari di utilizzo ed esempi dalla pagina PersistedAssemblyBuilder .

Assembly dinamici persistenti in .NET Framework

In .NET Framework gli assembly dinamici e i moduli possono essere salvati in file. Per supportare questa funzionalità, l'enumerazione AssemblyBuilderAccess dichiara due campi aggiuntivi: Save e RunAndSave.

I moduli dinamici nell'assembly dinamico persistente vengono salvati quando l'assembly dinamico viene salvato usando il Save metodo . Per generare un eseguibile, è necessario chiamare il SetEntryPoint metodo per identificare il metodo che rappresenta il punto di ingresso dell'assembly. Gli assembly vengono salvati come DLL per impostazione predefinita, a meno che il SetEntryPoint metodo non richieda la generazione di un'applicazione console o di un'applicazione basata su Windows.

Nell'esempio seguente viene illustrato come creare, salvare ed eseguire un assembly usando .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");
}

Alcuni metodi della classe base Assembly , ad esempio GetModules e GetLoadedModules, non funzioneranno correttamente quando vengono chiamati da AssemblyBuilder oggetti . È possibile caricare l'assembly dinamico definito e chiamare i metodi nell'assembly caricato. Ad esempio, per assicurarsi che i moduli delle risorse siano inclusi nell'elenco dei moduli restituiti, chiamare GetModules sull'oggetto caricato Assembly . Se un assembly dinamico contiene più moduli dinamici, il nome del file manifesto dell'assembly deve corrispondere al nome del modulo specificato come primo argomento del DefineDynamicModule metodo.

La firma di un assembly dinamico tramite KeyPair non è efficace fino a quando l'assembly non viene salvato su disco. Pertanto, i nomi sicuri non funzioneranno con assembly dinamici temporanei.

Gli assembly dinamici possono fare riferimento a tipi definiti in un altro assembly. Un assembly dinamico temporaneo può fare riferimento in modo sicuro a tipi definiti in un altro assembly dinamico temporaneo, a un assembly dinamico persistente o a un assembly statico. Tuttavia, Common Language Runtime non consente a un modulo dinamico persistente di fare riferimento a un tipo definito in un modulo dinamico temporaneo. Questo perché quando il modulo dinamico persistente viene caricato dopo essere stato salvato su disco, il runtime non può risolvere i riferimenti ai tipi definiti nel modulo dinamico temporaneo.

Restrizioni relative all'emissione di domini applicazione remoti

Alcuni scenari richiedono la creazione e l'esecuzione di un assembly dinamico in un dominio applicazione remoto. Reflection emit non consente l'emissione di un assembly dinamico direttamente in un dominio applicazione remoto. La soluzione consiste nel generare l'assembly dinamico nel dominio applicazione corrente, salvare l'assembly dinamico generato su disco e quindi caricare l'assembly dinamico nel dominio applicazione remoto. I domini remoti e dell'applicazione sono supportati solo in .NET Framework.