Sunteți pe pagina 1din 4

 

EJECUCIÓN AL VUELO DE CÓDIGO TECLEADO POR EL USUARIO 
Nivel: Intermedio  
por Alberto Población 

Con cierta frecuencia se repiten en los foros de .Net preguntas como la siguiente: 
 
 
¿Cómo podría hacer que mi programa permita al usuario escribir una fórmula matemática 
en una cadena de texto, como por ejemplo “1+2*Sin(0.5)” y luego obtener el resultado de 
la misma? 
 
 
O bien ciertas variantes de la misma pregunta tales como: 
 

Deseo  introducir  en  un  TextBox  una  ecuación  tal  como  “y=5*x^2‐3*x+1”  y  que  mi 
programa dibuje la gráfica de esta función. 

 
En todos los casos, se trata de que el programa sea capaz de interpretar y evaluar una 
expresión  que  sólo  se  conoce  en  tiempo  de  ejecución.  Lógicamente,  si  la  expresión 
fuera  conocida  en  tiempo  de  compilación,  bastaría  con  incorporarla  al  código  de 
nuestro  programa  y  el  compilador  generaría  sin  ningún  problema  las  instrucciones 
correspondientes. 

Del mismo modo, si deseamos hacerlo en un lenguaje interpretado que disponga ya de 
una instrucción para evaluar código (como por ejemplo el método eval de JavaScript), 
esta  tarea  no  tiene  la  más  mínima  dificultad.  Sin  embargo,  en  nuestro  caso 
presumimos que se quiere escribir el programa en uno de los lenguajes de .Net, tales 
como C# o VB.Net, que no aportan esta funcionalidad directamente. 

Para ello se nos presentan varias opciones: 

• Hacer  llamadas  a  algún  componente  externo  que  provea  esta  funcionalidad, 


como por ejemplo AxEval o el propio CScript. 

• Escribir un intérprete o compilador basado en un analizador sintáctico hecho 
por  nosotros  mismos.  Esta  vía  es  la  única  posible  cuando  la  sintaxis  de  las 
expresiones  que  queremos  admitir  no  coincide  con  la  de  un  lenguaje  de 
programación  ya  existente  y  accesible  desde  nuestra  aplicación.  Aunque 
parezca  muy  complicado,  es  razonablemente  fácil  si  las  expresiones  son  de 
cierta  sencillez.  En  un  futuro  artículo  presentaremos  esta  técnica  con  algún 
ejemplo simple. 
 

• Compilar la expresión sobre la marcha mediante uno de los compiladores de 
.Net,  y  a  continuación  ejecutar  el  código  que  hemos  compilado.  Esta  es  la 
técnica que elaboraremos en las líneas que siguen. 

 
Compilar las expresiones sobre la marcha 
Para aplicar el procedimiento consistente en compilar la expresión y llamar a la función 
compilada, realizaremos las siguientes operaciones: 

1. Construimos  un  pequeño  fragmento  de  código  fuente  que  tenga  la  sintaxis 
correcta  para  ser  compilado  con  el  compilador  del  lenguaje  elegido.  En  el 
ejemplo  que  se  incluye  más  abajo  hemos  declarado  una  clase  MiClase  que 
contiene una función  Evaluar, y dentro de la función hemos concatenado la 
cadena  introducida  por  el  usuario  (expresion).  Todo  ello  da  lugar  a  un 
string llamado fuente que contiene el código a compilar. 

2. Crear  una  instancia  del  compilador  llamando  al  método  estático 


CreateProvider de la clase CodeDomProvider. En el ejemplo concreto que 
acompaña  a  este  artículo  hemos  utilizado  el  compilador  CSharp,  pero  la 
técnica sería igualmente válida para Visual Basic. 

3. Crear una variable del tipo  CompilerParameters, en la que estableceremos 
los parámetros de compilación. En particular, añadiremos todas las referencias 
a  librerías  que  hayan  de  ser  enlazadas  con  nuestro  mini‐programa,  e 
indicaremos  que  el  resultado  de  la  compilación  sólo  ha  de  ser  temporal 
mediante la propiedad GenerateInMemory. 

4. Llamar  al  método  CompileAssemblyFromSource  de  nuestro  compilador, 


pasándole  los  CompilerParameters  y  el  código  fuente  que  antes  hemos 
construido. 

5. El resultado  es del  tipo  CompilerResults. Este objeto tiene  una propiedad 


llamada CompiledAssembly que nos devuelve el Assembly generado. 

6. Sobre  este  Assembly  utilizamos  las  habituales  técnicas  ya  conocidas  de 
System.Reflection  para  invocar  el  método  que  previamente  habíamos 
introducido en nuestro código fuente. El resultado devuelto por el método es 
precisamente el resultado de evaluar la expresión que nos interesaba. 

En el caso de que la expresión haya de ser evaluada repetidamente, como por ejemplo 
cuando tengamos que dibujar una gráfica de una función, todos los pasos necesarios 
para  la  compilación  basta  darlos  una  sola  vez.  Una  vez  compilada  la  expresión, 
podemos hacer llamadas repetidas para ir pintando la función punto por punto. Esto 
 

es  lo  que  hemos  hecho  en  nuestro  ejemplo,  que  presenta  una  clase  llamada 
Evaluador  cuyo  constructor  recibe  la  expresión  introducida  por  el  usuario  y  realiza 
todos  los  pasos  previos  para  llamarla.  Más  abajo,  contiene  una  función  llamada 
Evaluar que es la que llamaremos múltiples veces, cuando sea necesario realizar el 
cálculo de la expresión compilada. 

A  continuación  presentamos  el  listado  con  el  código  mencionado,  en  el  que  por 
claridad se han omitido todas las comprobaciones para control de errores: 
using System.CodeDom.Compiler;
using System.Reflection;
...
public class Evaluador
{
private MethodInfo mi;
private Object evaluador;

public Evaluador(string expresion)


{
string fuente = @"using System;
namespace MiCodigo {
class MiClase {
public double Evaluar(double x)
{
return " + expresion + @";
}
}
}";

CodeDomProvider compilador =
CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("system.dll");
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
CompilerResults cr =
compilador.CompileAssemblyFromSource(cp,
fuente);
Assembly a = cr.CompiledAssembly;
evaluador = a.CreateInstance("MiCodigo.MiClase",
false, System.Reflection.BindingFlags.CreateInstance, null,
new object[0], null, null);
Type t = evaluador.GetType();
mi = t.GetMethod("Evaluar");
}

public double Evaluar(double x)


{
object oRet = mi.Invoke(evaluador, new
object[]{x});
return Convert.ToDouble(oRet);
}
}
 

Ten en cuenta que cada vez que compilas con esta técnica se genera un ensamblado 
que queda cargado en memoria. El ejemplo anterior está destinado a dibujar la gráfica 
de una curva, por lo que la idea la función se compila una sola vez y luego se evalúa 
miles  de  veces.  Por  supuesto,  si  se  compilase  cada  una  de  las  veces,  sería  un  gran 
problema  porque  irían  quedando  multitud  de  ensamblados  en  memoria,  ocupando 
cada vez más y siendo totalmente desaconsejable.  

El  remedio  para  esto  es  cargarlos  todos  dentro  de  un  AppDomain  reservado  a  tal 
efecto.  Posteriormente,  cuando  ya  no  nos  hicieran  falta  las  funciones,  se  puede 
descargar de memoria el dominio e aplicación usando su método Unload. Quizá en un 
próximo  artículo  expliquemos  con  detalle  la  técnica  del  AppDomain,  pero  por  ahora 
debes quedarte con las ideas explicadas en este documento y trabajarlas con cuidado, 
pes pueden ser muy útiles. 

Acerca del autor 
Alberto  Población  lleva  27  años  desarrollando  software.  Ha  sido  reconocido  por  Microsoft 
como MVP (Most Valuable Professional) de C#. Cuenta, entre otras, con las certificaciones MCT, 
MCSE, MCDBA, MCITP, MCSD y MCPD en sus tres variantes (Desarrollador Web, Desarrollador 
Windows  y  Desarrollador  de  Aplicaciones  Empresariales).  En  la  actualidad  se  dedica 
principalmente  a  la  formación,  asesoramiento  y  desarrollo  de  aplicaciones.  Es  tutor  de 
campusMVP. 

Acerca de campusMVP 
CampusMVP te ofrece la mejor formación en tecnología Microsoft a través de nuestros cursos 
online y nuestros libros especializados, impartidos y escritos por conocidos MVP de Microsoft. 
Visita nuestra página y prueba nuestros cursos y libros gratuitamente. www‐campusmvp.com 
 
 
Reconocimiento ‐ NoComercial ‐ CompartirIgual (by‐nc‐sa):  
No se permite un uso comercial de este documento ni de las posibles obras derivadas, la 
distribución de las cuales se debe hacer con una licencia  igual a la que regula esta obra 
original. Se debe citar la fuente. 

S-ar putea să vă placă și