CodeDom

De la forma dinámica de hacer Código Dinámico

Por Willy Marroquín

Descargar ejemplos de este artículo (39 KB) Descargar ejemplos de este artículo (39 KB).

Contenido

 Introducción
 Un poco de historia ...
 El siguiente ejemplo sirve para darte cierta idea de lo que hace:
 Inside CodeDom (Code Document Object Model)
 El NameSpace System.CodeDom
 El código

Introducción

En algunas ocasiones, si se observa con detenimiento la manera como se ha construido software desde su inicio, ha evolucionado poco (por no decir casi nada) a la programación por automatización; es decir se define una algoritmia para un problema, se codifica, se compila y si aparece una nueva característica se codifica la misma y de nuevo se recompila, y así en lo sucesivo.

Dentro de las múltiples características existentes en el Framework, no podía faltar un NameSpace que permitiera tomar las riendas del sistema de compilación propiamente dicho, con el fin de generar y compilar código de manera dinámica. Si se ve con detenimiento esta postura, bien puede ser un primer paso hacia una forma de construcción distinta de aplicaciones. En las siguientes líneas, daremos un vistazo a este maquinar. Comentaremos un poco de CodeDom con VB .NET.

 

Un poco de historia ...

En versiones previas de Visual Basic, lo más cercano a la generación dinámica de código estaba representado por el Objeto VBIDE.VBE, que permitía leer, contar y escribir líneas de código de un componente en tiempo de ejecución. Esta técnica, bastante útil por cierto, se basaba en EOM (Extensibility Object Model).

 

El siguiente ejemplo sirve para darte cierta idea de lo que hace:

Esto asumiendo la existencia de dos cajas de texto llamadas TxT_Prj y TxT_Component
'en un proyecto VB 6.0 para  Winforms que permitiera leer la cantidad de líneas de
'un componente, como una reflexión primitiva que además permitiría compilar.
Option Explicit On
Public VBInstance As VBIDE.VBE
 
Private Sub cmdCountLines_Click()
   Dim objVBComponent As VBComponent
   objVBComponent = _
     VBInstance.VBProjects.Item(Txt_Prj.Text).VBComponents.Item(TxT_Component.Text)
   MsgBox(objVBComponent.CodeModule.CountOfLines)
End Sub

Bb972219.art220-img01-300x304(es-es,MSDN.10).gif
Figura 1: Arbol de grado 2. Volver al texto.

 

Inside CodeDom (Code Document Object Model)

Dejando de lado la nostalgia, y como indicábamos al comienzo, CodeDom toma al compilador "por el mango”.

CodeDom, utiliza una ya conocida fórmula que es la de basarse en árboles de grado dos. Los árboles de grado 2 (o binarios, tal como se les conoce de forma común; ver Figura 1) se definen como un conjunto finito de elementos (nodos) que bien está vacío o bien está formado por una raíz con dos árboles binarios disjuntos, llamados subárbol izquierdo y subárbol derecho de la raíz.

  • A. Representación típica de árbol.

  • B. Este se compone de Nodos que son piezas conceptuales para almacenamiento.

  • C. Nodo Padre o Nodo de Jerarquía Uno.

  • D. Representación de Nodo Raíz como parte de un árbol de grado 2 extensible.

  • E. Para localizar datos en un árbol este debe ser Cruzado. Al ser Cruzado se aplica algún algoritmo específico para la ubicación de datos en los Nodos de árbol.

Resulta importante entender estos fundamentos de árboles para poder entender de paso el NameSpace de CodeDom, pues justamente éste representa un tradicional árbol nivel 2 en memoria como los que alguna vez nos enseñaron en la Universidad ... Hace muchas líneas de código.

 

El NameSpace System.CodeDom

Para representar el código fuente, los elementos de System.CodeDom se vinculan entre sí formando una estructura de datos conocida como CodeDomGraph, que modela la estructura de parte del código fuente del cual se hará rendering. Adicionalmente, cada árbol existente de código a procesar se conoce como una Unidad de Compilación (CompileUnit).

Para tener una perspectiva clara del NameSpace, puedes referirte a ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.3082/cpref/html/frlrfsystemcodedomhierarchy.htm en la ayuda de Visual Studio 2003.

Pensemos que se quiere hacer Code Rendering a las siguientes líneas:

Namespace Samples
  Imports System
   Public Class Hello
    Public  Say  Sub New()
      Console.WriteLine("Hello World!")
    End Sub
   End Class
End Namespace

Esto representa el CodeGraph mostrado en la Figura 2.

Bb972219.art220-img02-502x391(es-es,MSDN.10).gif
Figura 2: Captura de pantalla de Vedex. Volver al texto.

Esto demuestra que CodeDom es análogo a los árboles de nivel 2. Las unidades de compilación (CompileUnit) son trabajadas por tres interfaces que serán las que hagan el trabajo duro.

ICodeParser: Esta interfaz se encarga de analizar el código recibido desde una unidad de compilación (CompileUnit) para colocarlo en Memoria.

ICodeGenerator: Esta interfaz lee las salidas generadas por ICodeParser y es quien genera el código fuente en el lenguaje deseado.

ICodeCompiler: Esta interfaz finalmente genera a nivel físico el ensamblado. Se suele combinar con System.CodeDom.Compiler.CompilerParameters, que asigna parámetros de compilación, de la misma forma que se reciben los parámetros de compilación por línea de comandos.

Más acción (compilación de ensamblados on-the-fly)

Pues ya basta de hablar. Vamos al código. En esta parte lo que haremos será tocar el primer nivel de utilización de CodeDom en la emisión dinámica de ensamblados y su ejecución de métodos. El proceso se divide en 4 partes.

Para demostrarlo crearemos un proyecto Winforms llamado ICodeCompiler y dentro del subdirectorio Bin de éste colocaremos un archivo llamado “CodigoaLeer.Txt” que contendrá el código a ejecutar (Ver Figura 3 y Figura 4).

Bb972219.art220-img03-332x289(es-es,MSDN.10).gif
Figura 3: . Volver al texto.

Bb972219.art220-img04-360x248(es-es,MSDN.10).gif
Figura 4: . Volver al texto.

 

El código

El siguiente es el código que utilizaremos para el render y está comentado por bloques:

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
   '**************************************************************************************
   'Paso A : Creación de Proveedor, Interfaz de Compilación, parámetros,
   '         Ensamblado, y String
   '**************************************************************************************
   Dim VBP As New VBCodeProvider
   Dim ICC As System.CodeDom.Compiler.IcodeCompiler
   Dim Parametro As New System.CodeDom.Compiler.CompilerParameters
   Dim Ensamblado As System.Reflection.Assembly
   Dim SB As New System.Text.StringBuilder
 
--------------------------------------------------------------------------------
 
   '**************************************************************************************
   'Paso B : Asignación de parámetro de Compilación, GenerateInMemory, GenerateExecutable,
   '         OutputAssembly, IncludeDebugInformation
   '**************************************************************************************
 
   With Parametro
      .GenerateInMemory = True
      '.GenerateExecutable = True
      .MainClass = "Prueba"
      .OutputAssembly = "ElResultado.Dll"
      .IncludeDebugInformation = False
   End With
 
   '**************************************************************************************
   'Paso C : Se leen las referencias de los ensamblados de los dominios de aplicación
   '         y se agrega a la colección de referencias de ensamblados al objeto parámetro
   '**************************************************************************************
 
   For Each Ensamblado In AppDomain.CurrentDomain.GetAssemblies
      Parametro.ReferencedAssemblies.Add(Ensamblado.Location)
   Next
 
   Try
      'Llamar al archivo de ejemplo y con el codigo a Compilar
      Dim Read As System.IO.TextReader
      Read = New System.IO.StreamReader(Application.StartupPath & "\CodigoaLeer.Txt")
      SB.Append(Read.ReadToEnd)
      Read.Close()
 
   Catch err As Exception
      'Por si algo sale mal =)
      MessageBox.Show(err.Message & " " & err.StackTrace)
   End Try
 
   '**************************************************************************************
   'Paso D :Generación del Ensamblado
   '**************************************************************************************
 
   'La interfaz se puebla desde el compilador del provider del lenguaje
   ICC = VBP.CreateCompiler
   Dim Results As System.CodeDom.Compiler.CompilerResults
   Results = ICC.CompileAssemblyFromSource(Parametro, SB.ToString)
 
   'El objeto Run lo utilizaremos para atrapar el resultado del objeto de Compilación
   'y no tener que hacer el llamado posterior con Reflexion, sino de forma directa
   Dim RunObj As New Object
   Dim vArgs() As Object
 
   RunObj = Results.CompiledAssembly.CreateInstance("elnamespace.prueba", _
     False, Reflection.BindingFlags.CreateInstance, Nothing, vArgs, Nothing, Nothing)
 
   If Not RunObj Is Nothing Then
      'llamar el metodo
      RunObj.MostarSaludo()
   Else
      MessageBox.Show("Fallo la compilación y el objeto results quedo vacío")
   End If
 
End Sub

Tras ejecutar este código se llama al único método existente que vino desde el archivo de texto, tal como se muestra en la Figura 5 y Figura 6 .

Bb972219.art220-img05-509x164(es-es,MSDN.10).gif
Figura 5: Llamado del método MostrarSaludo(). Volver al texto.

Bb972219.art220-img06-332x289(es-es,MSDN.10).gif
Figura 6: El resultado es el ensamblado llamado ElResultado.dll, asignado en el Paso A del listado general del ejercicio. Volver al texto.

Bien pueden imaginar que este es sólo el comienzo de esta propuesta. En el próximo artículo hablaremos de la generación dinámica de código fuente para que éste a su vez sea compilado y ejecutado, o dicho de otra forma, subiremos un peldaño más ...

Bb972219.willy_marroquin(es-es,MSDN.10).gif Willy Alexánder Marroquín ha trabajado 8 años en la construcción de software de manera profesional y está especializado en el desarrollo sobre la plataforma .net. Ha participado en varios proyectos que han sido galardonados a nivel internacional por Microsoft. Cuenta con las certificaciones MCP, MCSD, MCT y MCAD. Su otro frente son las Ciencias Humanas; está especializado en filosofía Nietzscheana y es estudiante de Psicología Social en la Universidad Nacional de Colombia.