Est-il possible de compiler et d'exécuter un nouveau code au moment de l'exécution en .NET?

StackOverflow https://stackoverflow.com/questions/234217

  •  04-07-2019
  •  | 
  •  

Question

Remarque: cette question ne porte pas sur l'évaluation de l'expression mathématique. Je souhaite compiler et exécuter un nouveau code au moment de l’exécution dans .NET. Cela dit ...

Je souhaite autoriser l'utilisateur à entrer n'importe quelle équation, telle que la suivante, dans une zone de texte:

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

Et appliquez cette équation aux points de données entrants. Les points de données entrants sont représentés par x et chaque point de données est traité par l'équation spécifiée par l'utilisateur. Je l’ai fait il ya des années, mais je n’aimais pas la solution car elle nécessitait l’analyse du texte de l’équation pour chaque calcul:

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

Lorsque vous traitez des cargaisons de bateaux de points de données, cela introduit un peu de temps système. J'aimerais pouvoir traduire l'équation en une fonction, à la volée, afin qu'elle ne soit analysée qu'une seule fois. Cela ressemblerait à quelque chose comme ça:

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

Function ConvertEquationToCode analyserait l'équation et renverrait un pointeur sur une fonction appliquant le calcul approprié.

L’application serait en train d’écrire du nouveau code au moment de l’exécution. Est-ce possible avec .NET?

Était-ce utile?

La solution

Oui! Utilisation de méthodes trouvées dans Microsoft.CSharp , System.CodeDom.Compiler et System.Reflection . Voici une application console simple qui compile une classe ("SomeClass") avec une méthode ("Add42") et vous permet ensuite d'appeler cette méthode. Ceci est un exemple simple que j'ai formaté pour empêcher les barres de défilement d'apparaître dans l'affichage du code. C’est juste pour démontrer la compilation et l’utilisation du nouveau code au moment de l’exécution.

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

Autres conseils

Vous pouvez essayer ceci: Calculator.Net

Il évaluera une expression mathématique.

À partir de la publication, il prend en charge les éléments suivants:

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

Page de téléchargement Et est distribué sous licence BSD.

Oui, il est tout à fait possible que l'utilisateur saisisse C # dans une zone de texte, puis compile ce code et l'exécute à partir de votre application. Nous faisons cela dans mon travail pour permettre une logique métier personnalisée.

Voici un article (je ne l'ai pas encore écrémé) qui devrait vous aider à démarrer:

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

Vous pouvez également créer un fichier System.Xml.XPath.XPathNavigator à partir d'un "dummy" vide. Flux XML, et évaluer des expressions à l'aide de l'évaluateur 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 vous souhaitez enregistrer des variables à utiliser dans cette expression, vous pouvez créer dynamiquement du XML que vous pouvez transmettre dans la surcharge d'évaluation cela prend un XPathNodeIterator.

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

Vous pouvez ensuite écrire des expressions telles que "x / 2 * 0.07914". puis x est la valeur du noeud dans votre contexte XML. Une autre bonne chose est que vous aurez accès à toutes les fonctions de base de XPath, qui comprend les mathématiques et les méthodes de manipulation de chaînes, et plus de choses.

Si vous voulez aller plus loin, vous pouvez même créer votre propre XsltCustomContext (ou poster ici sur demande) où vous pouvez résoudre les références aux fonctions d’extension et aux variables:

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

my: func est associé à une méthode C # / .NET qui prend un double ou un int comme paramètre. myvar est enregistré en tant que variable dans le contexte XSLT.

Vous pouvez essayer de regarder les arborescences d'expression CodeDom ou Lambda. Je pense que l’un ou l’autre de ces objectifs devrait vous permettre d’accomplir cela. Les arbres d’expression sont probablement la meilleure voie à suivre, mais ils ont également une courbe d’apprentissage plus rapide.

J'ai utilisé cela avec CSharpCodeProvider en créant la classe de la chaudière et les fonctions en tant que chaîne const dans ma classe de générateur. Ensuite, j'insère le code utilisateur dans la plaque de la chaudière et le compile.

C'était assez simple à faire, mais le danger de cette approche est que l'utilisateur qui saisit l'équation peut entrer à peu près tout ce qui pourrait poser un problème de sécurité en fonction de votre application.

Si la sécurité est un problème, je vous recommanderais d'utiliser des arbres d'expression Lambda, mais si ce n'est pas le cas, l'utilisation de CSharpCodeProvider est une option relativement robuste.

Vous pouvez commencer ici et si vous souhaitez vraiment y entrer. , Boo peut être modifié pour répondre à vos besoins. Vous pouvez également intégrer LUA avec .NET . Trois d'entre eux peuvent être utilisés dans le corps d'un délégué pour votre ConvertEquationToCode .

Avez-vous vu http://ncalc.codeplex.com ?

Il est extensible et rapide (il dispose par exemple de son propre cache) vous permet de fournir des fonctions personnalisées et des variantes au moment de l’exécution en gérant les événements EvaluateFunction / EvaluateParameter. Exemples d'expressions qu'il peut analyser:

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

Il gère également les codes unicode & amp; beaucoup de types de données nativement. Il vient avec un fichier de bois si vous voulez changer le grammeur. Il y a aussi un fork qui supporte MEF pour charger de nouvelles fonctions.

Essayez Vici.Parser: téléchargez-le ici (gratuitement) , c'est la plupart analyseur / évaluateur d'expression flexible que j'ai trouvé jusqu'à présent.

Si tout échoue, il existe des classes sous l'espace de noms System.Reflection.Emit que vous pouvez utiliser pour produire de nouveaux assemblys, classes et méthodes.

vous pouvez utiliser system.CodeDom pour générer du code et le compiler à la volée consultez ici

Vous pouvez implémenter un calculateur de pile postfix . Fondamentalement, vous devez convertir l’expression en notation postfixe, puis effectuer une itération sur les jetons de votre postfix pour calculer.

Voici une bibliothèque plus moderne pour les expressions simples: System.Linq.Dynamic.Core. Il est compatible avec .NET Standard / .NET Core, disponible via NuGet et le code source est disponible.

https: // système -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 /

Ceci est une bibliothèque très légère et dynamique.

J'ai écrit une classe de wrapper simple pour cette bibliothèque qui me permet de faire des choses comme celle-ci:

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

Une fois que l'expression est compilée, vous pouvez simplement mettre à jour les valeurs de paramètre et ré-invoquer l'expression.

Je ferais une fonction récursive qui n'écrit pas de code mais applique plutôt des opérateurs de base à des portions de chaîne basées sur des caractères spéciaux trouvés dans cette chaîne. Si plusieurs caractères spéciaux sont trouvés, il rompt la chaîne et s’appelle sur ces deux parties.

Je ne sais pas s'il est possible d'implémenter votre fonction ConvertEquationToCode . Toutefois, vous pouvez générer une structure de données qui représente le calcul que vous devez effectuer.

Par exemple, vous pouvez créer un arbre dont les noeuds feuille représentent l'entrée de votre calcul, dont les noeuds non-feuille représentent les résultats intermédiaires et dont le noeud racine représente le calcul complet.

Cela présente certains avantages. Par exemple, si vous effectuez une analyse de scénarios et souhaitez modifier la valeur d'une entrée à la fois, vous pouvez recalculer les résultats en fonction de la valeur que vous avez modifiée, tout en conservant les résultats qui ne le sont pas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top