Pregunta

Nota: la evaluación de expresiones matemáticas no es el foco de esta pregunta. Quiero compilar y ejecutar el nuevo código en tiempo de ejecución en .NET. Dicho esto ...

Me gustaría permitir que el usuario ingrese cualquier ecuación, como la siguiente, en un cuadro de texto:

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

Y aplique esa ecuación a los puntos de datos entrantes. Los puntos de datos entrantes se representan mediante x y cada punto de datos se procesa mediante la ecuación especificada por el usuario. Hice esto hace años, pero no me gustó la solución porque requería 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 estás procesando una gran cantidad de puntos de datos, esto introduce un poco de sobrecarga. Me gustaría poder traducir la ecuación en una función, sobre la marcha, de modo que solo tenga que ser analizada una vez. Se vería así:

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

La función ConvertEquationToCode analizaría la ecuación y devolvería un puntero a una función que aplique las matemáticas apropiadas.

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

¿Fue útil?

Solución

¡Sí! Utilizando métodos encontrados en Microsoft.CSharp , System.CodeDom.Compiler , y System.Reflection espacios de nombres. 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 básico que he formateado para evitar que aparezcan barras de desplazamiento en la pantalla de código. Es solo para demostrar la compilación y el uso del 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();
        }
    }
}

Otros consejos

Puede intentar esto: Calculator.Net

Evaluará una expresión matemática.

Desde la publicación, admitirá 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");

Página de descarga Y se distribuye bajo la licencia BSD.

Sí, definitivamente es posible que el usuario escriba C # en un cuadro de texto, luego compile ese código y ejecútelo desde su aplicación. Lo hacemos en mi trabajo para permitir la lógica empresarial personalizada.

Aquí hay un artículo (no lo he visto más que descremado) que debería comenzar:

http://www.c-sharpcorner.com/UploadFile/ ChrisBlake / RunTimeCompiler12052005045037AM / RunTimeCompiler.aspx

También puede crear un System.Xml.XPath.XPathNavigator a partir de un vacío, '' ficticio '' Flujo XML, y evalúa 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 variables para usar dentro de esta expresión, puede generar dinámicamente XML que puede pasar en la sobrecarga de Evaluar eso requiere un XPathNodeIterator.

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

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

Si desea llevarlo más lejos, incluso puede crear su propio XsltCustomContext (o publicarlo aquí a pedido) donde puede resolver referencias a funciones de extensión y variables:

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

my: func se asigna a un método C # / .NET que toma un doble o int como parámetro. myvar se registra como una variable dentro del contexto XSLT.

Puedes intentar ver los árboles de expresión CodeDom o Lambda. Creo que cualquiera de esos debería permitirte lograr esto. Los árboles de expresión son probablemente el mejor camino a seguir, pero también tienen una curva de aprendizaje más alta.

He hecho esto usando CSharpCodeProvider creando la clase de placa de caldera y cosas de función como una cadena constante 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 ingresa 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 una preocupación, recomendaría usar Lambda Expression Trees, pero si no, CSharpCodeProvider es una opción bastante sólida.

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

¿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 al manejar eventos EvaluateFunction / EvaluateParameter. Ejemplos de expresiones 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 & amp; muchos tipos de datos de forma nativa. Viene con un archivo de asta si desea cambiar la gramática. También hay un tenedor que soporta MEF para cargar nuevas funciones.

Pruebe Vici.Parser: descárguelo aquí (gratis) , es el más Analizador / evaluador de expresión flexible que he encontrado hasta ahora.

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

puede usar system.CodeDom para generar código y compilarlo sobre la marcha Echa un vistazo a aquí

Podría implementar una calculadora de pila de postfix . Básicamente, lo que tienes que hacer es convertir la expresión a notación de postfix, y luego simplemente iterar sobre los tokens en tu postfix para calcular.

Aquí una biblioteca más moderna para expresiones simples: System.Linq.Dynamic.Core. Es compatible con .NET Standard / .NET Core, está disponible a través de NuGet y la fuente está disponible.

https: // system -linq-dynamic-core.azurewebsites.net/html/de47654c-7ae4-9302-3061-ea6307706cb8.htm https://github.com/StefH/System.Linq.Dynamic.Core https://www.nuget.org/packages/System.Linq.Dynamic .Core /

Esta es una biblioteca muy ligera y dinámica.

Escribí una clase de contenedor simple para esta biblioteca que me permite hacer cosas como esta:

  string sExpression = "(a == 0) ? 5 : 10";
  ExpressionEvaluator<int> exec = new ExpressionEvaluator<int>(sExpression);
  exec.AddParameter("a", 0);
  int n0 = exec.Invoke();

Una vez que se compila la expresión, simplemente puede actualizar los valores de los parámetros y volver a invocar la expresión.

Haría una función recursiva que no escribe código, sino que aplica operadores básicos a partes de una cadena basada en caracteres especiales que se encuentran en esa cadena. Si se encuentra más de un carácter especial, divide la cadena y se llama a sí misma en esas dos partes.

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

Por ejemplo, podría construir un árbol cuyos nodos hoja representan la entrada para su cálculo, cuyos nodos no 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 "qué pasaría si" y quiere cambiar el valor de una entrada a la vez, puede volver a calcular los resultados que dependen del valor que ha cambiado, mientras mantiene los resultados que no lo hacen.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top