Domanda

Nota: la valutazione dell'espressione matematica non è al centro di questa domanda. Voglio compilare ed eseguire il nuovo codice in fase di esecuzione in .NET. Detto questo ...

Vorrei consentire all'utente di inserire qualsiasi equazione, come la seguente, in una casella di testo:

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

E quell'equazione viene applicata ai punti dati in arrivo. I punti dati in arrivo sono rappresentati da x e ciascun punto dati viene elaborato dall'equazione specificata dall'utente. L'ho fatto anni fa, ma non mi piaceva la soluzione perché richiedeva l'analisi del testo dell'equazione per ogni calcolo:

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

Quando si elaborano carichi di punti dati in battello, ciò comporta un certo sovraccarico. Vorrei poter tradurre l'equazione in una funzione, al volo, in modo che debba essere analizzata una sola volta. Sarebbe simile a questo:

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

La funzione ConvertEquationToCode analizzerebbe l'equazione e restituirebbe un puntatore a una funzione che applica la matematica appropriata.

L'app in pratica scriverà nuovo codice in fase di esecuzione. È possibile con .NET?

È stato utile?

Soluzione

Sì! Utilizzando i metodi trovati in Microsoft.CSharp , System.CodeDom.Compiler e System.Reflection spazi dei nomi. Ecco una semplice app console che compila una classe (" SomeClass ") con un metodo (" Add42 ") e quindi ti permette di invocare quel metodo. Questo è un esempio semplice che ho formattato per evitare che le barre di scorrimento vengano visualizzate nella visualizzazione del codice. È solo per dimostrare la compilazione e l'utilizzo di nuovo codice in fase di esecuzione.

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

Altri suggerimenti

Potresti provare questo: Calculator.Net

Valuterà un'espressione matematica.

Dalla pubblicazione supporterà quanto segue:

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

Pagina di download Ed è distribuito sotto la licenza BSD.

Sì, sicuramente è possibile avere l'utente digitare C # in una casella di testo, quindi compilare quel codice ed eseguirlo dall'app. Lo facciamo nel mio lavoro per consentire una logica aziendale personalizzata.

Ecco un articolo (non l'ho più che scremato) che dovrebbe iniziare:

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

Puoi anche creare un System.Xml.XPath.XPathNavigator da un vuoto, " dummy " Flusso XML, e valuta le espressioni usando il valutatore 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 ( );

Se desideri registrare le variabili da utilizzare all'interno di questa espressione, puoi creare dinamicamente XML che puoi passare nel sovraccarico di valutazione che richiede un XPathNodeIterator.

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

Puoi quindi scrivere espressioni come " x / 2 * 0,07914 " e poi x è il valore del nodo nel tuo contesto XML. Un'altra cosa positiva è che avrai accesso a tutte le funzioni principali di XPath, che include metodi matematici e di manipolazione delle stringhe e altro ancora.

Se vuoi andare oltre, puoi persino creare il tuo XsltCustomContext (o post malato qui su richiesta) dove puoi risolvere riferimenti a funzioni e variabili di estensione:

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

my: func è mappato su un metodo C # /. NET che accetta un parametro double o int. myvar è registrato come variabile nel contesto XSLT.

Puoi provare a guardare CodeDom o Lambda Expression Trees. Penso che uno di questi dovrebbe permetterti di raggiungere questo obiettivo. Gli alberi delle espressioni sono probabilmente il modo migliore per andare ma hanno anche una curva di apprendimento più elevata.

Ho fatto questo usando CSharpCodeProvider creando la classe della piastra della caldaia e funzioni come una stringa const all'interno della mia classe del generatore. Quindi inserisco il codice utente nella piastra della caldaia e compilo.

È stato abbastanza semplice da fare, ma il pericolo per questo approccio è che l'utente che immette l'equazione potrebbe inserire qualsiasi cosa che possa costituire un problema di sicurezza a seconda dell'applicazione.

Se la sicurezza è un problema, consiglierei di utilizzare Lambda Expression Trees, ma in caso contrario l'utilizzo di CSharpCodeProvider è un'opzione abbastanza solida.

Potresti iniziare qui e se vuoi davvero approfondire , Boo può essere modificato per soddisfare le tue esigenze. Puoi anche integrare LUA con .NET . Tre di questi potrebbero essere utilizzati nel corpo di un delegato per il tuo ConvertEquationToCode .

Hai visto http://ncalc.codeplex.com ?

È estensibile, veloce (ad es. ha una propria cache) che consente di fornire funzioni personalizzate e variabili in fase di esecuzione gestendo gli eventi EvaluateFunction / EvaluateParameter. Espressioni di esempio che può analizzare:

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

Gestisce anche unicode & amp; molti tipi di dati sono nativi. Viene fornito con un file antler se si desidera modificare la grammatica. C'è anche un fork che supporta MEF per caricare nuove funzioni.

Prova Vici.Parser: scaricalo qui (gratuito) , è il più parser / valutatore di espressioni flessibili che ho trovato finora.

Se tutto il resto fallisce, ci sono classi nello spazio dei nomi System.Reflection.Emit che puoi usare per produrre nuovi assembly, classi e metodi.

puoi usare system.CodeDom per generare codice e compilarlo al volo dai un'occhiata qui

Potresti implementare un calcolatore di stack postfix . Fondamentalmente quello che devi fare è convertire l'espressione in notazione postfix e quindi semplicemente scorrere i token nel tuo postfix per calcolare.

Ecco una libreria più moderna per espressioni semplici: System.Linq.Dynamic.Core. È compatibile con .NET Standard / .NET Core, è disponibile tramite NuGet e l'origine è disponibile.

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 /

Questa è una libreria molto leggera e dinamica.

Ho scritto una semplice classe wrapper per questa libreria che mi permette di fare cose del genere:

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

Una volta compilata l'espressione, è possibile semplicemente aggiornare i valori dei parametri e richiamare nuovamente l'espressione.

Farei una funzione ricorsiva che non scrive codice ma applica invece operatori di base a porzioni di una stringa in base a caratteri speciali trovati in quella stringa. Se viene trovato più di un carattere speciale, spezza la stringa e si chiama su quelle due parti.

Non so se sia possibile implementare la funzione ConvertEquationToCode , tuttavia, è possibile generare una struttura di dati che rappresenta il calcolo che è necessario eseguire.

Ad esempio, è possibile creare un albero i cui nodi foglia rappresentano l'input per il calcolo, i cui nodi non foglia rappresentano risultati intermedi e il cui nodo radice rappresenta l'intero calcolo.

Ha alcuni vantaggi. Ad esempio, se si esegue un'analisi what-if e si desidera modificare il valore di un input alla volta, è possibile ricalcolare i risultati che dipendono dal valore che è stato modificato, mantenendo i risultati che non lo sono.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top