Compilar en tiempo de ejecución

Hola a todos, seguro que muchos de vosotros ya sabéis de que va todo esto, pero es que es una de las cosas que más gracia me hizo cuando descubrí cómo hacerlo. Vamos a ver cómo gracias a una librería muy maja (System.CodeDom) podemos compilar en tiempo de ejecución y gracias a la reflexión (ya sabéis que es la raíz de todo mal) podemos ejecutarlo.

 

El uso que se le puede dar a esto es prácticamente ilimitado aunque no es tan fácil de ver (al menos en mí caso), así que me encantaría que me dijeseis para qué lo habéis usado para hacerme una idea, en mí caso yo lo tuve que usar para permitir que ciertos usuarios bastante avanzados pudieran meter validaciones complejas sobre campos y se ejecutasen al momento, vamos para hacer un generador de fórmulas y cosas así está muy bien.

 

Bueno os pongo un ejemplito para que veáis lo fácil que es (la verdad es que los chicos de .NET son unos tipos muy listos).

 

La función que genera una clase en tiempo de ejecución que tiene un método que ejecuta un “Hola Mundo!”

private object CreaYCompilaHolaMundoClass()
{
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("system.dll");
cp.ReferencedAssemblies.Add("system.data.dll");
cp.ReferencedAssemblies.Add("system.xml.dll");
cp.ReferencedAssemblies.Add("system.windows.forms.dll");

cp.GenerateExecutable = false;
cp.GenerateInMemory = true;


StringBuilder code = new StringBuilder();
code.Append("using System; n");
code.Append("using System.Windows.Forms; n");
code.Append("using System.Data; n");
code.Append("using System.Xml; n");
code.Append("using System.Reflection; n");
code.Append("namespace Prueba.Compilador { n");
code.AppendFormat(" public class {0} ", "HolaMundo");
code.Append("{ ");
//Clases de apoyo
//GetValue a string
code.Append(" public void HazTuTrabajo() n");
code.Append(" {");
code.Append(" MessageBox.Show("Hola mundo");");
code.Append(" }n");

code.Append("} }");

CompilerResults cr = CodeDomProvider.CreateProvider("C#").CompileAssemblyFromSource(cp, code.ToString());
if (cr.Errors.HasErrors)
{
StringBuilder error = new StringBuilder();
error.Append("Error al compilar: ");
foreach (CompilerError err in cr.Errors)
{
error.AppendFormat("{0}n", err.ErrorText);
}
throw new Exception("Error al compilar: " + error.ToString());
}
Assembly a = cr.CompiledAssembly;
return a.CreateInstance("Prueba.Compilador.HolaMundo");
}

 

Lo primero importante es que necesita el using del System.CodeDom.Compiler. Luego cosas a destacar, ReferencedAssemblies son las dlls que queremos referenciar desde nuestro código generado (en el ejemplo he puesto varios aunque no se necesitan todos), en este caso no se genera una dll física si no que está compilada en memoria.

Después escribimos el código que queramos que tenga (hay que tener un poco de cuidado con las llaves de apertura y cierre) y la línea de CompilerResults….. es la importante. Después de eso se genera un Assembly (System.Reflection) y se devuelve una instancia…

 

Para ejecutar lo que hemos creado sería algo similar a esto

object _comp = CreaYCompilaHolaMundoClass();
MethodInfo mi = _comp.GetType().GetMethod("HazTuTrabajo");
mi.Invoke(_comp, null);

 

Y poco más… evidentemente esto se puede complicar lo que queramos, pasando parámetros y demás… como os dije antes se abren muchas, muchísimas puertas aunque hay veces que es complicado verle la utilidad.

 

Por cierto, yo no opino que usar reflexión sea malo, siempre y cuando sea usada correctamente (creo que hay muchas discusiones sobre esto pero algún día me gustaría escribir sobre ello) la reflexión mola y mola mucho.

 

Un saludo y hasta la próxima.

Mario Ropero.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *