2008-10-24 14 views
33

Nota: La evaluación de la expresión matemática no es el foco de esta pregunta. Quiero compilar y ejecutar código nuevo en tiempo de ejecución en .NET. Dicho esto ...¿Es posible compilar y ejecutar código nuevo en tiempo de ejecución en .NET?

me gustaría para permitir al usuario introducir cualquier ecuación, como la siguiente, en un cuadro de texto:

x = x/2 * 0.07914 
x = x^2/5 

y tener esa ecuación se aplica a los puntos de datos entrantes. Los puntos de datos entrantes están representados por x y cada punto de datos es procesado por la ecuación especificada por el usuario. Hice esto hace años, pero no me gusta la solución, ya que requiere analizar el texto de la ecuación para cada cálculo:

float ApplyEquation (string equation, float dataPoint) 
{ 
    // parse the equation string and figure out how to do the math 
    // lots of messy code here... 
} 

Cuando se está procesando botes llenos de puntos de datos, esto introduce un poco de gastos generales. Me gustaría poder traducir la ecuación en una función, sobre la marcha, de modo que solo tenga que analizarse una vez. Se vería algo como esto:

FunctionPointer foo = ConvertEquationToCode(equation); 
.... 
x = foo(x); // I could then apply the equation to my incoming data like this 

Función ConvertEquationToCode sería analizar la ecuación y devolver un puntero a una función que se aplica la matemática apropiada.

La aplicación básicamente escribiría un nuevo código en tiempo de ejecución. ¿Es esto posible con .NET?

+0

duplicado posible de [¿Cómo puedo evaluar una expresión de C# de forma dinámica?] (Http://stackoverflow.com/questions/53844/how-can-i-evaluate-ac-sharp-expression-dynamically) – Ridkuma

Respuesta

18

Sí! Utilizando métodos encontrados en los espacios de nombres Microsoft.CSharp, System.CodeDom.Compiler y System.Reflection. Aquí hay una aplicación de consola simple que compila una clase ("SomeClass") con un método ("Add42") y luego le permite invocar ese método. Este es un ejemplo escueto que he formateado para evitar que las barras de desplazamiento aparezcan en la visualización del código. Es solo para demostrar la compilación y el uso de un nuevo código en tiempo de ejecución.

using Microsoft.CSharp; 
using System; 
using System.CodeDom.Compiler; 
using System.Reflection; 

namespace RuntimeCompilationTest { 
    class Program 
    { 
     static void Main(string[] args) { 
      string sourceCode = @" 
       public class SomeClass { 
        public int Add42 (int parameter) { 
         return parameter += 42; 
        } 
       }"; 
      var compParms = new CompilerParameters{ 
       GenerateExecutable = false, 
       GenerateInMemory = true 
      }; 
      var csProvider = new CSharpCodeProvider(); 
      CompilerResults compilerResults = 
       csProvider.CompileAssemblyFromSource(compParms, sourceCode); 
      object typeInstance = 
       compilerResults.CompiledAssembly.CreateInstance("SomeClass"); 
      MethodInfo mi = typeInstance.GetType().GetMethod("Add42"); 
      int methodOutput = 
       (int)mi.Invoke(typeInstance, new object[] { 1 }); 
      Console.WriteLine(methodOutput); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

Sin embargo, esto es muy lento para expresiones aritméticas simples. –

+2

@JonathanNappee: Estaba feliz de descubrir cómo hacer esto, no dije que fuera genial. ¡Me encantaría ver tus mejoras de rendimiento! – raven

2

Puede intentar mirar CodeDom o Lambda Expression Trees. Creo que cualquiera de esos debería permitirle lograr esto. Los árboles de expresión son probablemente la mejor opción pero también tienen una curva de aprendizaje más alta.

-1

Haría una función recursiva que no escriba código sino que aplique operadores básicos a porciones de una cadena basadas en caracteres especiales encontrados en esa cadena. Si se encuentra más de un carácter especial, divide la cadena y se llama a sí mismo en esas dos partes.

+0

Lo que quieres es un analizador de expresiones. Hice esto una vez convirtiendo la expresión de infijo a postfix y luego usando un analizador de posfix basado en pila. Los algoritmos para ambos están bien definidos y existen en la web y en los libros. – Nick

2

Puede comenzar here y si realmente desea entrar en él, Boo puede modificarse para satisfacer sus necesidades. También puede integrar LUA with .NET. Cualquiera de estos tres podría ser utilizado dentro del cuerpo de un delegado para su ConvertEquationToCode.

11

Usted puede tratar esto: Calculator.Net

Se va a evaluar una expresión matemática.

Desde la publicación que apoyará lo siguiente:

MathEvaluator eval = new MathEvaluator(); 
//basic math 
double result = eval.Evaluate("(2 + 1) * (1 + 2)"); 
//calling a function 
result = eval.Evaluate("sqrt(4)"); 
//evaluate trigonometric 
result = eval.Evaluate("cos(pi * 45/180.0)"); 
//convert inches to feet 
result = eval.Evaluate("12 [in->ft]"); 
//use variable 
result = eval.Evaluate("answer * 10"); 
//add variable 
eval.Variables.Add("x", 10);    
result = eval.Evaluate("x * 10"); 

Download Page y se distribuye bajo la licencia BSD.

+2

Querrá ver el espacio de nombres CodeDom y luego, es posible compilar el código en tiempo de ejecución. – cfeduke

+0

Parece que esta biblioteca tiene algunos errores y parece que todavía no es compatible. –

3

He hecho esto usando CSharpCodeProvider creando la clase de placa de la caldera y cosas de la función como una cadena const dentro de mi clase de generador. Luego inserto el código de usuario en la placa de la caldera y compilo.

Fue bastante sencillo de hacer, pero el peligro de este enfoque es que el usuario que ingrese la ecuación podría ingresar casi cualquier cosa que podría ser un problema de seguridad dependiendo de su aplicación.

Si la seguridad es en absoluto una preocupación, recomendaría utilizar Lambda Expression Trees, pero si no, usar CSharpCodeProvider es una opción bastante robusta.

5

También puede crear un System.Xml.XPath.XPathNavigator de una, secuencia XML vacío "ficticia", y evaluar expresiones usando el evaluador XPath:

static object Evaluate (string xp) 
{ 
    return _nav.Evaluate (xp); 
} 
static readonly System.Xml.XPath.XPathNavigator _nav 
    = new System.Xml.XPath.XPathDocument (
     new StringReader ("<r/>")).CreateNavigator (); 

Si desea registrar las variables a utilizar dentro de esta expresión, puede construir dinámicamente XML que puede pasar en la sobrecarga Evaluar que toma un XPathNodeIterator.

<context> 
    <x>2.151</x> 
    <y>231.2</y> 
</context> 

A continuación, puede escribir expresiones como "x/2 * 0,07914" y entonces x es el valor del nodo en su contexto XML. Otra cosa buena es que tendrá acceso a todas las funciones básicas de XPath, , que incluye métodos matemáticos y de manipulación de cadenas, y más.

Si quieres ir más lejos, incluso se puede construir su propia XsltCustomContext (o mal puesto aquí en la demanda) donde se puede resolver referencias a funciones de extensión y variables:

object result = Evaluate ("my:func(234) * $myvar"); 

mi: func es mapeado a un método C# /. NET que toma un parámetro doble o int como. myvar está registrado como una variable dentro del contexto XSLT.

0

Si falla todo lo demás, hay clases en el espacio de nombres System.Reflection.Emit que puede usar para producir nuevos ensamblados, clases y métodos.

+0

Sin embargo, una palabra de advertencia. Usamos esto en .Less (dotlesscss.com) y emitir código generalmente significa un rendimiento horrible. –

-2

No sé si es posible implementar su función ConvertEquationToCode, sin embargo, puede generar una estructura de datos que represente el cálculo que necesita realizar.

Por ejemplo, podría compilar un árbol cuyos nodos de hoja representan la entrada para su cálculo, cuyos nodos de hoja representan resultados intermedios y cuyo nodo raíz representa el cálculo completo.

Tiene algunas ventajas. Por ejemplo, si está haciendo un análisis de "y si" y desea cambiar el valor de una entrada a la vez, puede volver a calcular los resultados que dependen del valor que ha modificado, conservando los resultados que no lo hacen.

1

Pruebe Vici.Parser: download it here (free), es el analizador/evaluador de expresiones más flexible que he encontrado hasta ahora.

0

puede utilizar para generar código system.CodeDom y compilarlo sobre la marcha echar un vistazo here

0

Se podría implementar un postfix stack calculator.Básicamente lo que tienes que hacer es convertir la expresión en notación de postfijo, y luego simplemente iterar sobre los tokens en tu postfijo para calcular.

3

¿Has visto http://ncalc.codeplex.com?

Es extensible, rápido (por ejemplo, tiene su propio caché) le permite proporcionar funciones personalizadas y variables en tiempo de ejecución mediante el manejo de eventos EvaluateFunction/EvaluateParameter. Expresiones de ejemplo que puede analizar:

Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)"); 

    e.Parameters["Pi2"] = new Expression("Pi * Pi"); 
    e.Parameters["X"] = 10; 

    e.EvaluateParameter += delegate(string name, ParameterArgs args) 
    { 
     if (name == "Pi") 
     args.Result = 3.14; 
    }; 

    Debug.Assert(117.07 == e.Evaluate()); 

También maneja Unicode & muchos tipo de datos de forma nativa. Viene con un archivo de astas si quieres cambiar la gramática. También hay un tenedor que soporta MEF para cargar nuevas funciones.

+0

¿por qué el voto abajo? – GreyCloud

Cuestiones relacionadas