Una posible solución consiste en
“compilar” la expresión
. Se recorre el diagrama, igual que enel caso de la interpretación, pero cada vez que hay que realizar una de las operaciones decálculo, lo que se hace, en lugar de operar sobre la marcha, es generar código ejecutable queimplemente esa operación.Terminado el diagrama, y generado todo el código, las sucesivas ejecuciones se realizanmediante llamadas a dicho ejecutable,
esta vez a toda velocidad
.Para este fin, vamos a utilizar las clases disponibles en el espacio de nombres
System.Reflection.Emit
. Aunque es posible generar un ensamblado dinámico o salvarlo adisco, para la aplicación concreta que tenemos entre manos es preferible utilizar lo que sedenomina un
DynamicMethod
, que viene a ser un método generado dinámicamente enmemoria y que podemos ejecutar sobre la marcha.
La ventaja del método dinámico es que el
Garbage Collector
es capaz de liberarlo cuando ya no se necesite
, mientras que si se generaun ensamblado, no se descarga de memoria mientras no se descargue el dominio de aplicacióncompleto.Los pasos necesarios para generar el método dinámico son estos:1.
Definir un delegado que sirva para apuntar a un método del tipo que queremosgenerar. En nuestro ejemplo utilizaremos el tipo
DelegadoParaEvaluar
que sirvepara apuntar a métodos que reciban como argumento un
double
y devuelvan comoresultado otro
double
.2.
Crear una instancia de la clase
DynamicMethod
, pasándole en el constructor elnombre del método y los
Type
de los argumentos y el resultado.3.
Llamar al método
GetILGenerator
del
DynamicMethod
para obtener una instanciadel generador de código
IL (“
Intermediate Language
”).
4.
Llamar al método
Emit
del generador cuantas veces sea necesario para ir escribiendoel código de nuestro método dinámico. Esta es la operación que se va realizandorepetidamente cada vez que en el diagrama sintáctico se ve la necesidad de realizaruna operación. Los argumentos de
Emit
indican cuál es la operación concreta que seva a realizar.5.
Llamar al método
CreateDelegate
del
DynamicMethod
para obtener un delegado,que el que finalmente se usa para ejecutar el código generado.En el ejemplo de código que se adjunta como
Listado 1
a continuación, hemos reproducido ensu mayor parte el analizador sintáctico
que ya introdujimos en el artículo sobre el intérprete,pero
hemos sustituido las rutinas que ejecutaban las operaciones matemáticas
(
OperacionSuma
,
OperacionResta
, etc.)
por otras equivalentes que generan código
mediante
Reflection.Emit
.El
Listado 2
muestra la forma de
realizar la llamada para evaluar una expresión
.