Generación Dinámica de Código I
Un tema en el que estoy últimamente muy metido por cuestiones del trabajo es en la generación dinámica de código y la forma en que esta se puede automatizar para crear ficheros de código fuente en lenguajes soportados por el CLR.
Para conseguir esto necesitamos básicamente apoyarnos en dos pilares, por un lado debemos generar un árbol en el que se recoja toda la parte sintáctica y semántica del programa fuente que queremos construir (independiente del lenguaje de programación) para ello utilizaremos las clases que podemos encontrar bajo el espacio de nombre System.CodeDom. Por otro lado, una vez que ya tenemos el árbol podemos pasar a generar el código asociado al mismo en un lenguaje especifico de programación soportado por el CLR ( de este modo, en un solo paso podemos generar el mismo programa en varios lenguajes como veremos más adelante).
En el espacio de nombres System.CodeDom podemos encontrar clases que nos representan cada una de las estructuras posibles que nos podemos encontrar en cualquier programa (Clases, Métodos, Propiedades, Enumeraciones, Parámetros, estructuras repetitivas, asignaciones, etc. etc...)
La raíz de nuestro arbol será un objeto del tipo CodeCompileUnit el cual recogerá una serie de objetos del tipo CodeNamespace el cual a su vez contiene una seria de Imports (objetos CodeNamespaceImport, utlizados para "importar" namespaces) y Types (una colección de objetos CodeTypeDeclaration, los cuales pueden representar código de clases, estructuras, interfaces y enumeraciones)
Finalmente para la generación de un programa en un lenguaje determinado necesitamos un generador de código, para ello utilizaremos cualquier clase que implemente la interface ICodeGenerator como es la clase CodeDomProvider. (localizada en el espacio de nombres System.CodeDom.Compiler). Para cada lenguaje especifico tenemos un proveedor (CSharpCodeProvider, VBCodeProvider, JScriptCodeProvider, VJSharpCodeProvider).
Veamos una serie de ejemplos de como podemos explotar esta funcionalidad:
Crear un espacio de nombres:
// Declara el NameSpace indicando el nombre del mismo.
CodeNamespace espacioNombres = new CodeNamespace("MiEspacioNombres");
//Espacios de nombres a importar
CodeNamespaceImport import = new CodeNamespaceImport("System.CodeDom");
//Añadimos el espacio de nombres
espacioNombres.Imports.Add(import);
Crear una clase:
CodeTypeDeclaration clase = new CodeTypeDeclaration("MiClase");
//Indicamos que es clase.
clase.IsClass = true;
//Indicamos la visibilidad
clase.Attributes = MemberAttributes.Public;
//Añadimos a la clase sus elementos (atributos, propiedades, métodos…)
clase.Members.Add(new CodeTypeMember());
//Añadimos la clase al espacio de nombres.
espacioNombres.Types.Add(clase);
Ahora pasaremos a ver como generar el código y crear un fichero con el mismo.
Este ejemplo lo haremos para lenguaje C#, para cualquier otro habría simplemente que cambiar el proveedor.
CodeCompileUnit ccu = new CodeCompileUnit();
//Generador de codigo que vamos a utilizar para un determinado lenguaje.
CodeDomProvider proveedor;
ccu.Namespaces.Add(espacioNombres);
//Generador de codigo que vamos a usar
proveedor = new CSharpCodeProvider();
//Guardamos la extension que debe tener el archivo
string extension = proveedor.FileExtension;
//Creamos un TextWriter con el cual vamos a guardar el codigo generado
TextWriter t = new StreamWriter(rutaDestino+"\\"+nombreFichero+"."+extension, false);
//Finalmente generamos el codigo
proveedor.GenerateCodeFromCompileUnit(ccu, t, new CodeGeneratorOptions());
//Cerramos el fichero.
t.Close();
En proximos post comentaremos como utilizar más de estas estructuras...