Frage

Hinweis: Mathematischer Ausdruck Auswertung ist nicht im Mittelpunkt dieser Frage. Ich möchte kompilieren und zu neuem Code zur Laufzeit in .NET ausführen. Dass gesagt wird ...

Ich möchte dem Benutzer erlauben, jede Gleichung einzugeben, wie die folgenden, in ein Textfeld:

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

Und haben diese Gleichung auf eingehende Datenpunkte angewendet. Die eingehenden Datenpunkte werden durch repräsentiert x und jeder Datenpunkt durch den Benutzer angegebenen Gleichung verarbeitet wird. Ich habe vor Jahren, aber ich habe nicht die Lösung gefällt weil es erforderlich, für jede Berechnung der Text der Gleichung Parsen:

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

Wenn Sie boatloads von Datenpunkten verarbeiten, führt dies ziemlich viel Aufwand. Ich möchte in der Lage sein, die Gleichung in eine Funktion, on the fly zu übersetzen, so dass es nur einmal analysiert werden muss. Es würde wie folgt aussehen:

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

Funktion ConvertEquationToCode würde die Gleichung analysieren und einen Zeiger auf eine Funktion zurück, die die entsprechende Mathematik gilt.

Die App wäre grundsätzlich neuen Code zur Laufzeit zu schreiben. Ist dies möglich mit .NET?

War es hilfreich?

Lösung

Ja! Unter Verwendung gefunden Methoden in dem Microsoft.CSharp , System.CodeDom.Compiler und System.Reflection Namensräume. Hier ist eine einfache Konsolenanwendung, die eine Klasse ( „Someclass“) mit einer Methode ( „Add42“) kompiliert und dann können Sie diese Methode aufzurufen. Dies ist ein Barebone-Beispiel, das ich formatierte bis zu Bildlaufleisten erscheinen in der Code-Anzeige zu verhindern. Es ist nur zu zeigen, zusammenzustellen und um neuen Code zur Laufzeit verwendet wird.

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();
        }
    }
}

Andere Tipps

Sie könnten versuchen, diese: Calculator.Net

Es wird einen mathematischen Ausdruck bewerten.

Von der Entsendung unterstützt es die folgende:

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-Seite Und unter der BSD-Lizenz vertrieben.

Ja, auf jeden Fall möglich, den Benutzertyp C # in einem Textfeld zu haben, dann diesen Code kompilieren und es aus Ihrer Anwendung ausführen. Wir tun das bei meiner Arbeit für benutzerdefinierte Geschäftslogik zu ermöglichen.

Hier ist ein Artikel (ich habe nicht mehr, als es abgeschöpft), die sollten Sie beginnen:

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

Sie können auch eine System.Xml.XPath.XPathNavigator aus einem leeren, "Dummy" XML-Stream erstellen, und Ausdrücke auswerten des XPath-Evaluator:

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 ( );

Wenn Sie Variablen registrieren in diesem Ausdruck zu verwenden, Sie können dynamisch XML erstellen, die Sie in der Evaluate Überlastung passieren kann das dauert XPathNodeIterator.

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

Sie können dann Ausdrücke schreiben wie „x / 2 * 0,07914“ und dann x ist der Wert des Knotens in der XML-Kontext. Eine weitere gute Sache ist, haben Sie Zugriff auf alle XPath Kernfunktionen haben, welche umfasst Mathematik und String-Manipulationsmethoden und mehr Material.

Wenn Sie es weiter gehen möchten, können Sie sogar Ihre eigenen XsltCustomContext bauen (oder krank Post hier auf Anfrage) wo Sie Verweise auf Erweiterungsfunktionen und Variablen auflösen können:

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

meine. Func ist mit einem C # / NET Verfahren abgebildet, die ein Doppel- oder ein int als Parameter übernimmt. myvar wird als Variable in dem XSLT-Kontext registriert.

Sie können versuchen, entweder CodeDom oder Lambda Expression Trees suchen. Ich denke, entweder einer von denen sollte Ihnen ermöglichen, dies zu erreichen. Expression Bäume sind wahrscheinlich der bessere Weg zu gehen, sondern auch eine höhere Lernkurve hat.

Ich habe diese mit CSharpCodeProvider erfolgt durch das Kesselblech Klassen- und Funktions Zeug als const string in meiner Generator-Klasse erstellen. Dann füge ich den Benutzercode in das Kesselblech und kompilieren.

Es war recht einfach zu tun, aber die Gefahr dieses Ansatzes ist, dass der Benutzer die Gleichung Eingabe nur um etwas geben könnte, die ein Sicherheitsproblem je nach Anwendung sein könnte.

Wenn die Sicherheit bei allen ein Anliegen ist, würde ich empfehlen Lambda Expression Baum benutzt, aber wenn nicht, ist CSharpCodeProvider mit relativ robuste Option.

Sie könnten beginnen hier und wenn Sie wirklich in sie erhalten möchten , Boo können geändert werden, um Ihre Bedürfnisse zu erfüllen. Sie können auch LUA mit .NET integrieren. Alle drei dieser könnte im Körper eines Delegierten für Ihre ConvertEquationToCode genutzt werden.

Haben Sie gesehen http://ncalc.codeplex.com ?

Es ist erweiterbar, schnell (zum Beispiel verfügt über einen eigenen Cache) können Sie benutzerdefinierte Funktionen zur Verfügung zu stellen und varaibles zur Laufzeit durch EvaluateFunction / EvaluateParameter Behandlung von Ereignissen. Beispiel Ausdrücke kann es analysieren:

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()); 

Es behandelt auch Unicode und viele Daten nativ geben. Es kommt mit einem Geweih-Datei, wenn Sie die grammer ändern möchten. Es gibt auch eine Gabel, die MEF neue Funktionen laden unterstützt.

Versuchen Vici.Parser: es hier herunterladen (kostenlos) , ist es die flexible Ausdrucksparser / Auswerter ich bisher gefunden habe.

Wenn alles andere fehlschlägt, gibt es Klassen unter dem System.Reflection.Emit Namespace, die Sie neue Baugruppen produzieren können, Klassen und Methoden.

Sie können system.CodeDom verwenden, um Code zu generieren und on the fly kompilieren haben Sie einen Blick hier

könnten Sie implementieren einen Postfix-Stack-Rechner . Im Grunde, was Sie tun müssen, ist, den Ausdruck zu Postfixnotation konvertieren, und dann einfach durchlaufen die Token in Ihrem Postfix zu berechnen.

Hier eine moderne Bibliothek für einfache Ausdrücke: System.Linq.Dynamic.Core. Es ist kompatibel mit .NET Standard / .NET Core es durch NuGet verfügbar ist, und die Quelle zur Verfügung.

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 /

Dies ist eine sehr leichte und dynamische Bibliothek.

schrieb ich eine einfache Wrapper-Klasse für diese Bibliothek, die das lassen Sie mich Dinge tun, wie folgt aus:

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

Wenn der Ausdruck kompiliert wird, können Sie einfach die Parameterwerte aktualisieren und erneut aufrufen, um den Ausdruck.

würde ich eine rekursive Funktion tun, die keinen Programmcode schreiben, sondern gilt Basisoperatoren auf Teile einer Zeichenfolge basierend auf Sonderzeichen in dieser Zeichenfolge gefunden. Wenn mehr als ein Sonderzeichen gefunden wird, bricht es die Zeichenfolge und nennt sich auf diese beiden Bereiche.

Ich weiß nicht, ob es möglich ist, Ihre ConvertEquationToCode Funktion zu implementieren, jedoch können Sie eine Datenstruktur erzeugen, die die Berechnung Sie ausführen müssen, darstellt.

Zum Beispiel könnten Sie einen Baum, dessen Blattknoten repräsentiert die Eingabe für die Berechnung bauen, deren Nicht-Blattknoten darstellen Zwischenergebnisse und der Wurzelknoten repräsentiert die gesamte Berechnung.

Es hat einige Vorteile. Zum Beispiel, wenn Sie das tun, was-wäre-wenn-Analyse und wollen den Wert eines Eingangs zu einem Zeitpunkt ändern, können Sie die Ergebnisse neu berechnet werden können, die auf dem Wert ab, die Sie geändert haben, während die Ergebnisse beibehalten, die dies nicht tun.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top