¿Es posible comstackr y ejecutar código nuevo en tiempo de ejecución en .NET?

Nota: La evaluación de la expresión matemática no es el foco de esta pregunta. Quiero comstackr y ejecutar código nuevo en tiempo de ejecución en .NET. Habiendo dicho eso…

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 tenga esa ecuación aplicada 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. Lo hice 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 procesas una gran cantidad de puntos de datos, esto introduce bastante sobrecarga. 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 

La función ConvertEquationToCode analizará la ecuación y devolverá un puntero a una función que aplica la matemática adecuada.

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

¡Sí! Utilizando métodos encontrados en los espacios de nombre de Microsoft.CSharp , System.CodeDom.Compiler y System.Reflection . Aquí hay una aplicación de consola simple que comstack 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 comstackció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 RuntimeComstacktionTest { 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(); } } } 

Puede intentar esto: Calculator.Net

Evaluará una expresión matemática.

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

Descargar la página 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. Hacemos eso en mi trabajo para permitir la lógica de negocios personalizada.

Aquí hay un artículo (no lo he descuidado) que debería comenzar:

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

También puede crear un System.Xml.XPath.XPathNavigator desde una secuencia XML vacía, “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 ( "" ) ).CreateNavigator ( ); 

Si desea registrar variables para usar dentro de esta expresión, puede construir dinámicamente XML que pueda pasar en la sobrecarga de evaluación que toma un XPathNodeIterator.

  2.151 231.2  

Luego 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ás acceso a todas las funciones centrales de XPath, que incluyen métodos matemáticos y de manipulación de cadenas, y más.

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

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

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

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

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

Es extensible, rápido (por ejemplo, tiene su propio caché) le permite proporcionar funciones personalizadas y varaibles en tiempo de ejecución mediante el manejo de 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 y muchos tipos 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.

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.

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

Prueba Vici.Parser: descárgalo aquí (gratis) , es el analizador / evaluador de expresiones más 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 comstackrlo sobre la marcha eche un vistazo aquí

Podría implementar una calculadora de stack postfija . 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.

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: // http://www.nuget.org/packages/System.Linq.Dynamic.Core/

Esta es una biblioteca muy liviana y dinámica.

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

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

Una vez que se comstack 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 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.

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 construir un árbol cuyos nodos de hoja representen 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.