Clase System.Reflection.Emit.AssemblyBuilder

En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.

Un ensamblado dinámico es un ensamblado que se crea mediante las API de emisión de Reflexiones ion. Un ensamblado dinámico puede hacer referencia a tipos definidos en otro ensamblado dinámico o estático. Puede usar AssemblyBuilder para generar ensamblados dinámicos en memoria y ejecutar su código durante la misma ejecución de la aplicación. En .NET 9 agregamos un nuevo persistedAssemblyBuilder con una implementación totalmente administrada de emisión de reflexión que le permite guardar el ensamblado en un archivo. En .NET Framework, puede ejecutar el ensamblado dinámico y guardarlo en un archivo. El ensamblado dinámico creado para guardar se denomina ensamblado persistente , mientras que el ensamblado de solo memoria normal se denomina transitorio o ejecutable. En .NET Framework, un ensamblado dinámico puede constar de uno o varios módulos dinámicos. En .NET Core y .NET 5+, un ensamblado dinámico solo puede constar de un módulo dinámico.

La forma en que crea una AssemblyBuilder instancia difiere para cada implementación, pero los pasos adicionales para definir un módulo, tipo, método o enumeración, y para escribir IL, son bastante similares.

Ensamblados dinámicos ejecutables en .NET

Para obtener un objeto ejecutable AssemblyBuilder , use el AssemblyBuilder.DefineDynamicAssembly método . Los ensamblados dinámicos se pueden crear mediante uno de los siguientes modos de acceso:

El modo de acceso debe especificarse proporcionando el valor adecuado AssemblyBuilderAccess en la llamada al AssemblyBuilder.DefineDynamicAssembly método cuando se define el ensamblado dinámico y no se puede cambiar más adelante. El tiempo de ejecución usa el modo de acceso de un ensamblado dinámico para optimizar la representación interna del ensamblado.

En el ejemplo siguiente se muestra cómo crear y ejecutar un ensamblado:

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 }));
}

Ensamblados dinámicos persistentes en .NET

El nuevo PersistedAssemblyBuilder tipo derivado de AssemblyBuilder y permite guardar los ensamblados dinámicos en .NET Core, compruebe los escenarios de uso y los ejemplos de la página PersistedAssemblyBuilder .

Ensamblados dinámicos persistentes en .NET Framework

En .NET Framework, los ensamblados dinámicos y los módulos se pueden guardar en archivos. Para admitir esta característica, la AssemblyBuilderAccess enumeración declara dos campos adicionales: Save y RunAndSave.

Los módulos dinámicos del ensamblado dinámico persistente se guardan cuando el ensamblado dinámico se guarda mediante el Save método . Para generar un archivo ejecutable, SetEntryPoint se debe llamar al método para identificar el método que es el punto de entrada del ensamblado. Los ensamblados se guardan como archivos DLL de forma predeterminada, a menos que el SetEntryPoint método solicite la generación de una aplicación de consola o una aplicación basada en Windows.

En el ejemplo siguiente se muestra cómo crear, guardar y ejecutar un ensamblado mediante .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");
}

Algunos métodos de la clase base Assembly , como GetModules y GetLoadedModules, no funcionarán correctamente cuando se llama desde AssemblyBuilder objetos . Puede cargar el ensamblado dinámico definido y llamar a los métodos en el ensamblado cargado. Por ejemplo, para asegurarse de que los módulos de recursos se incluyen en la lista de módulos devueltos, llame a GetModules en el objeto cargado Assembly . Si un ensamblado dinámico contiene más de un módulo dinámico, el nombre del archivo de manifiesto del ensamblado debe coincidir con el nombre del módulo especificado como primer argumento para el DefineDynamicModule método.

La firma de un ensamblado dinámico mediante KeyPair no es efectiva hasta que el ensamblado se guarda en el disco. Por lo tanto, los nombres seguros no funcionarán con ensamblados dinámicos transitorios.

Los ensamblados dinámicos pueden hacer referencia a tipos definidos en otro ensamblado. Un ensamblado dinámico transitorio puede hacer referencia de forma segura a tipos definidos en otro ensamblado dinámico transitorio, un ensamblado dinámico persistente o un ensamblado estático. Sin embargo, Common Language Runtime no permite que un módulo dinámico persistente haga referencia a un tipo definido en un módulo dinámico transitorio. Esto se debe a que cuando se carga el módulo dinámico persistente después de guardarse en el disco, el tiempo de ejecución no puede resolver las referencias a los tipos definidos en el módulo dinámico transitorio.

Restricciones en la emisión a dominios de aplicación remotos

Algunos escenarios requieren que se cree y ejecute un ensamblado dinámico en un dominio de aplicación remota. Reflexiones emisión no permite emitir un ensamblado dinámico directamente a un dominio de aplicación remota. La solución consiste en emitir el ensamblado dinámico en el dominio de aplicación actual, guardar el ensamblado dinámico emitido en el disco y, a continuación, cargar el ensamblado dinámico en el dominio de aplicación remota. Los dominios de comunicación remota y de aplicación solo se admiten en .NET Framework.