Comment puis-je évaluer le code C # dynamiquement?


92

Je peux faire un eval("something()");pour exécuter le code dynamiquement en JavaScript. Y a-t-il un moyen pour moi de faire la même chose en C #?

Un exemple de ce que j'essaie de faire est: J'ai une variable entière (disons i) et j'ai plusieurs propriétés par les noms: "Propriété1", "Propriété2", "Propriété3", etc. Maintenant, je veux effectuer quelques opérations sur la propriété "Property i " en fonction de la valeur de i.

C'est vraiment simple avec Javascript. Existe-t-il un moyen de le faire avec C #?



2
c # appelle eval ironpython. Je l'ai essayé en c # 4.0. aucune expérience avec c # 2.0
Peter Long

@Peter Long, où puis-je trouver de la documentation sur l'évaluation d'IronPython?
smartcaveman

@AdhipGupta Je sais que ce Q&R est assez daté, mais je viens de publier une playlist vidéo qui ressemble beaucoup à la description donnée dans la réponse de Davide Icardi. C'est radicalement différent et vaut probablement le détour.
Rick Riggs

Réponses:


49

Malheureusement, C # n'est pas un langage dynamique comme ça.

Ce que vous pouvez faire, cependant, est de créer un fichier de code source C #, plein de classe et de tout, et de l'exécuter via le fournisseur CodeDom pour C # et de le compiler dans un assembly, puis de l'exécuter.

Ce message de forum sur MSDN contient une réponse avec un exemple de code en bas de la page:
créer une méthode anonyme à partir d'une chaîne?

Je dirais à peine que c'est une très bonne solution, mais c'est possible de toute façon.

À quel type de code allez-vous vous attendre dans cette chaîne? S'il s'agit d'un sous-ensemble mineur de code valide, par exemple simplement des expressions mathématiques, il se peut que d'autres alternatives existent.


Edit : Eh bien, cela m'apprend à lire attentivement les questions d'abord. Oui, la réflexion pourrait vous aider ici.

Si vous divisez la chaîne par le; Tout d'abord, pour obtenir des propriétés individuelles, vous pouvez utiliser le code suivant pour obtenir un objet PropertyInfo pour une propriété particulière d'une classe, puis utiliser cet objet pour manipuler un objet particulier.

String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);

Link: PropertyInfo.SetValue, méthode


et si GetProperty doit être une fonction avec des paramètres x, y?, signifie Texte (1, 2)?
user1735921

@ user1735921 Ensuite, vous devrez utiliser à la GetMethod(methodName)place, analyser les valeurs des paramètres et appeler la méthode en utilisant la réflexion.
Lasse V.Karlsen

typeof (ObjectType) est analogue à someObject.GetType ()
Thiago

34

Utilisation de l'API de script Roslyn (plus d' exemples ici ):

// add NuGet package 'Microsoft.CodeAnalysis.Scripting'
using Microsoft.CodeAnalysis.CSharp.Scripting;

await CSharpScript.EvaluateAsync("System.Math.Pow(2, 4)") // returns 16

Vous pouvez également exécuter n'importe quel morceau de code:

var script = await CSharpScript.RunAsync(@"
                class MyClass
                { 
                    public void Print() => System.Console.WriteLine(1);
                }")

Et référencez le code généré lors des exécutions précédentes:

await script.ContinueWithAsync("new MyClass().Print();");

14

Pas vraiment. Vous pouvez utiliser la réflexion pour réaliser ce que vous voulez, mais ce ne sera pas aussi simple qu'en Javascript. Par exemple, si vous souhaitez définir le champ privé d'un objet sur quelque chose, vous pouvez utiliser cette fonction:

protected static void SetField(object o, string fieldName, object value)
{
   FieldInfo field = o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
   field.SetValue(o, value);
}

11

C'est une fonction eval sous c #. Je l'ai utilisé pour convertir des fonctions anonymes (expressions Lambda) à partir d'une chaîne. Source: http://www.codeproject.com/KB/cs/evalcscode.aspx

public static object Eval(string sCSCode) {

  CSharpCodeProvider c = new CSharpCodeProvider();
  ICodeCompiler icc = c.CreateCompiler();
  CompilerParameters cp = new CompilerParameters();

  cp.ReferencedAssemblies.Add("system.dll");
  cp.ReferencedAssemblies.Add("system.xml.dll");
  cp.ReferencedAssemblies.Add("system.data.dll");
  cp.ReferencedAssemblies.Add("system.windows.forms.dll");
  cp.ReferencedAssemblies.Add("system.drawing.dll");

  cp.CompilerOptions = "/t:library";
  cp.GenerateInMemory = true;

  StringBuilder sb = new StringBuilder("");
  sb.Append("using System;\n" );
  sb.Append("using System.Xml;\n");
  sb.Append("using System.Data;\n");
  sb.Append("using System.Data.SqlClient;\n");
  sb.Append("using System.Windows.Forms;\n");
  sb.Append("using System.Drawing;\n");

  sb.Append("namespace CSCodeEvaler{ \n");
  sb.Append("public class CSCodeEvaler{ \n");
  sb.Append("public object EvalCode(){\n");
  sb.Append("return "+sCSCode+"; \n");
  sb.Append("} \n");
  sb.Append("} \n");
  sb.Append("}\n");

  CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
  if( cr.Errors.Count > 0 ){
      MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText, 
         "Error evaluating cs code", MessageBoxButtons.OK, 
         MessageBoxIcon.Error );
      return null;
  }

  System.Reflection.Assembly a = cr.CompiledAssembly;
  object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");

  Type t = o.GetType();
  MethodInfo mi = t.GetMethod("EvalCode");

  object s = mi.Invoke(o, null);
  return s;

}

1
@sehe Oups, j'ai corrigé la faute de frappe (Lambada => Lambda). Je ne savais pas que la chanson s'appelait Lambada, donc celle-ci n'était pas intentionnelle. ;)
Largo

Je ne pouvais pas comprendre pourquoi cette réponse obtenait moins de voix. C'est très utile.
Muzaffer Galata

9

J'ai écrit un projet open source, Dynamic Expresso , qui peut convertir une expression de texte écrite en utilisant une syntaxe C # en délégués (ou arbre d'expression). Les expressions sont analysées et transformées en arborescences d'expression sans utiliser de compilation ou de réflexion.

Vous pouvez écrire quelque chose comme:

var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");

ou

var interpreter = new Interpreter()
                      .SetVariable("service", new ServiceExample());

string expression = "x > 4 ? service.SomeMethod() : service.AnotherMethod()";

Lambda parsedExpression = interpreter.Parse(expression, 
                          new Parameter("x", typeof(int)));

parsedExpression.Invoke(5);

Mon travail est basé sur l'article de Scott Gu http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .


7

Tout cela fonctionnerait certainement. Personnellement, pour ce problème particulier, j'adopterais probablement une approche un peu différente. Peut-être quelque chose comme ça:

class MyClass {
  public Point point1, point2, point3;

  private Point[] points;

  public MyClass() {
    //...
    this.points = new Point[] {point1, point2, point3};
  }

  public void DoSomethingWith(int i) {
    Point target = this.points[i+1];
    // do stuff to target
  }
}

Lorsque vous utilisez des modèles comme celui-ci, vous devez faire attention à ce que vos données soient stockées par référence et non par valeur. En d'autres termes, ne faites pas cela avec les primitives. Vous devez utiliser leurs grands homologues de classe gonflés.

J'ai réalisé que ce n'était pas exactement la question, mais la question a été assez bien répondue et j'ai pensé qu'une approche alternative pourrait peut-être aider.


5

Je ne le fais pas maintenant si vous voulez absolument exécuter des instructions C #, mais vous pouvez déjà exécuter des instructions Javascript en C # 2.0. La bibliothèque open-source Jint est capable de le faire. C'est un interpréteur Javascript pour .NET. Passez un programme Javascript et il fonctionnera dans votre application. Vous pouvez même passer un objet C # comme arguments et faire une automatisation dessus.

Aussi, si vous souhaitez simplement évaluer l'expression sur vos propriétés, essayez NCalc .


3

Vous pouvez utiliser la réflexion pour obtenir la propriété et l'invoquer. Quelque chose comme ça:

object result = theObject.GetType().GetProperty("Property" + i).GetValue(theObject, null);

Autrement dit, en supposant que l'objet qui a la propriété s'appelle "theObject" :)


2

Vous pouvez également implémenter un navigateur Web, puis charger un fichier html contenant du javascript.

Ensuite, vous allez pour la document.InvokeScriptméthode sur ce navigateur. La valeur de retour de la fonction eval peut être capturée et convertie en tout ce dont vous avez besoin.

J'ai fait cela dans plusieurs projets et cela fonctionne parfaitement.

J'espère que ça aide


0

Vous pouvez le faire avec une fonction prototype:

void something(int i, string P1) {
    something(i, P1, String.Empty);
}

void something(int i, string P1, string P2) {
    something(i, P1, P2, String.Empty);
}

void something(int i, string P1, string P2, string P3) {
    something(i, P1, P2, P3, String.Empty);
}

etc...



0

J'ai écrit un package, SharpByte.Dynamic , pour simplifier la tâche de compilation et d'exécution du code de manière dynamique. Le code peut être appelé sur n'importe quel objet de contexte en utilisant des méthodes d'extension comme détaillé plus loin ici .

Par exemple,

someObject.Evaluate<int>("6 / {{{0}}}", 3))

renvoie 3;

someObject.Evaluate("this.ToString()"))

renvoie la représentation sous forme de chaîne de l'objet de contexte;

someObject.Execute(@
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");

exécute ces instructions sous forme de script, etc.

Les exécutables peuvent être obtenus facilement à l'aide d'une méthode de fabrique, comme le montre l'exemple ici - tout ce dont vous avez besoin est le code source et la liste de tous les paramètres nommés attendus (les jetons sont incorporés en utilisant une notation à trois crochets, telle que {{{0}}) }, pour éviter les collisions avec string.Format () ainsi que les syntaxes de type Handlebars):

IExecutable executable = ExecutableFactory.Default.GetExecutable(executableType, sourceCode, parameterNames, addedNamespaces);

Chaque objet exécutable (script ou expression) est thread-safe, peut être stocké et réutilisé, prend en charge la journalisation à partir d'un script, stocke les informations de synchronisation et la dernière exception si elle est rencontrée, etc. Il existe également une méthode Copy () compilée sur chacun pour permettre créer des copies bon marché, c'est-à-dire utiliser un objet exécutable compilé à partir d'un script ou d'une expression comme modèle pour en créer d'autres.

La surcharge liée à l'exécution d'un script ou d'une instruction déjà compilé est relativement faible, bien en dessous d'une microseconde sur du matériel modeste, et les scripts et expressions déjà compilés sont mis en cache pour être réutilisés.


0

J'essayais d'obtenir une valeur d'un membre de structure (classe) par son nom. La structure n'était pas dynamique. Toutes les réponses n'ont pas fonctionné jusqu'à ce que je l'ai enfin obtenue:

public static object GetPropertyValue(object instance, string memberName)
{
    return instance.GetType().GetField(memberName).GetValue(instance);
}

Cette méthode retournera la valeur du membre par son nom. Cela fonctionne sur une structure régulière (classe).


0

Vous pouvez consulter la bibliothèque Heleonix.Reflection . Il fournit des méthodes pour obtenir / définir / appeler des membres de manière dynamique, y compris des membres imbriqués, ou si un membre est clairement défini, vous pouvez créer un getter / setter (lambda compilé dans un délégué) qui est plus rapide que la réflexion:

var success = Reflector.Set(instance, null, $"Property{i}", value);

Ou si le nombre de propriétés n'est pas infini, vous pouvez générer des setters et les chacher (les setters sont plus rapides puisqu'ils sont des délégués compilés):

var setter = Reflector.CreateSetter<object, object>($"Property{i}", typeof(type which contains "Property"+i));
setter(instance, value);

Les setters peuvent être de type Action<object, object>mais les instances peuvent être différentes lors de l'exécution, vous pouvez donc créer des listes de setters.


-1

Malheureusement, C # n'a pas de fonctionnalités natives pour faire exactement ce que vous demandez.

Cependant, mon programme C # eval permet d'évaluer le code C #. Il permet d'évaluer le code C # au moment de l'exécution et prend en charge de nombreuses instructions C #. En fait, ce code est utilisable dans n'importe quel projet .NET, cependant, il est limité à l'utilisation de la syntaxe C #. Jetez un œil à mon site Web, http://csharp-eval.com , pour plus de détails.


Jetez un œil à Roslyn Scripting API
AlexMelw

-9

la bonne réponse est que vous devez mettre en cache tous les résultats pour réduire l'utilisation de la mémoire.

un exemple ressemblerait à ceci

TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
} 

et ajoutez-le à une liste

List<string> results = new List<string>();
for() results.Add(result);

enregistrez l'identifiant et utilisez-le dans le code

J'espère que cela t'aides


5
quelqu'un a confondu l'évaluation avec la recherche. Si vous connaissez tous les programmes possibles (je pense que c'est au moins NP-Hard) ... et que vous avez une supermachine pour précompiler tous les résultats possibles ... et il n'y a pas d'effets secondaires / d'entrées externes ... Ouais, cette idée fonctionne théoriquement . Le code est une grande erreur de syntaxe, bien
sehe
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.