Wie kann ich C # -Code dynamisch auswerten?

92

Ich kann eval("something()");den Code dynamisch in JavaScript ausführen. Gibt es eine Möglichkeit für mich, dasselbe in C # zu tun?

Ein Beispiel für das, was ich versuche, ist: Ich habe eine ganzzahlige Variable (sagen wir i) und ich habe mehrere Eigenschaften mit den Namen: "Property1", "Property2", "Property3" usw. Nun möchte ich einige Operationen ausführen auf der Eigenschaft "Property i " abhängig vom Wert von i.

Dies ist mit Javascript wirklich einfach. Gibt es eine Möglichkeit, dies mit C # zu tun?

Adhip Gupta
quelle
2
c # Ironpython's Eval nennen. Ich habe es in c # 4.0 versucht. Keine Erfahrung mit C # 2.0
Peter Long
@Peter Long, wo finde ich Dokumentation zu IronPythons Bewertung?
Smartcaveman
@AdhipGupta Ich weiß, dass diese Fragen und Antworten ziemlich veraltet sind, aber ich habe gerade eine Video-Wiedergabeliste veröffentlicht , die der Beschreibung in Davide Icardis Antwort sehr ähnlich ist. Es ist dramatisch anders und wahrscheinlich einen Besuch wert.
Rick Riggs

Antworten:

49

Leider ist C # keine solche dynamische Sprache.

Sie können jedoch eine C # -Quellcodedatei mit Klasse und allem erstellen, diese über den CodeDom-Anbieter für C # ausführen, zu einer Assembly kompilieren und dann ausführen.

Dieser Forumsbeitrag auf MSDN enthält eine Antwort mit einem Beispielcode auf der Seite:
Erstellen Sie eine anonyme Methode aus einer Zeichenfolge?

Ich würde kaum sagen, dass dies eine sehr gute Lösung ist, aber es ist trotzdem möglich.

Welche Art von Code erwarten Sie in dieser Zeichenfolge? Wenn es sich um eine kleine Teilmenge gültigen Codes handelt, beispielsweise nur um mathematische Ausdrücke, gibt es möglicherweise andere Alternativen.


Edit : Nun, das lehrt mich, die Fragen zuerst gründlich zu lesen. Ja, Reflexion kann Ihnen hier helfen.

Wenn Sie die Zeichenfolge durch die; Um einzelne Eigenschaften abzurufen, können Sie zunächst den folgenden Code verwenden, um ein PropertyInfo-Objekt für eine bestimmte Eigenschaft für eine Klasse abzurufen, und dann mit diesem Objekt ein bestimmtes Objekt bearbeiten.

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

Link: PropertyInfo.SetValue-Methode

Lasse V. Karlsen
quelle
Was ist, wenn GetProperty eine Funktion mit x, y-Parametern sein muss? Bedeutet Text (1,2)?
user1735921
@ user1735921 Dann müssen Sie GetMethod(methodName)stattdessen verwenden, die Parameterwerte analysieren und die Methode mithilfe von Reflection aufrufen.
Lasse V. Karlsen
typeof (ObjectType) ist analog zu someObject.GetType ()
Thiago
32

Verwenden der Roslyn-Skript-API (weitere Beispiele hier ):

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

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

Sie können auch einen beliebigen Code ausführen:

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

Verweisen Sie auf den Code, der in früheren Läufen generiert wurde:

await script.ContinueWithAsync("new MyClass().Print();");
Eli Arbel
quelle
14

Nicht wirklich. Sie können Reflexion verwenden, um das zu erreichen, was Sie wollen, aber es ist nicht annähernd so einfach wie in Javascript. Wenn Sie beispielsweise das private Feld eines Objekts auf etwas setzen möchten, können Sie diese Funktion verwenden:

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

Dies ist eine Bewertungsfunktion unter c #. Ich habe es verwendet, um anonyme Funktionen (Lambda-Ausdrücke) aus einer Zeichenfolge zu konvertieren. Quelle: 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;

}
Largo
quelle
1
@sehe Whoops, ich habe den Tippfehler korrigiert (Lambada => Lambda). Ich wusste nicht, dass das Lied Lambada heißt, also war dieses unbeabsichtigt. ;)
Largo
Ich konnte nicht verstehen, warum diese Antwort weniger Stimmen bekommt. Es ist sehr nützlich.
Muzaffer Galata
9

Ich habe ein Open-Source-Projekt geschrieben, Dynamic Expresso , das Textausdrücke, die mit einer C # -Syntax geschrieben wurden, in Delegaten (oder Ausdrucksbäume) konvertieren kann. Ausdrücke werden analysiert und ohne Kompilierung oder Reflexion in Ausdrucksbäume umgewandelt .

Sie können so etwas schreiben wie:

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

oder

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

Meine Arbeit basiert auf dem Artikel von Scott Gu http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .

Davide Icardi
quelle
7

All das würde definitiv funktionieren. Persönlich würde ich für dieses spezielle Problem wahrscheinlich einen etwas anderen Ansatz wählen. Vielleicht so etwas:

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
  }
}

Wenn Sie solche Muster verwenden, müssen Sie darauf achten, dass Ihre Daten als Referenz und nicht als Wert gespeichert werden. Mit anderen Worten, tun Sie dies nicht mit Grundelementen. Sie müssen ihre großen aufgeblähten Klassenkollegen verwenden.

Mir wurde klar, dass dies nicht genau die Frage ist, aber die Frage wurde ziemlich gut beantwortet und ich dachte, vielleicht könnte ein alternativer Ansatz helfen.

MojoFilter
quelle
5

Ich weiß es jetzt nicht, wenn Sie unbedingt C # -Anweisungen ausführen möchten, aber Sie können Javascript-Anweisungen bereits in C # 2.0 ausführen. Die Open-Source-Bibliothek Jint ist dazu in der Lage. Es ist ein Javascript-Interpreter für .NET. Übergeben Sie ein Javascript-Programm und es wird in Ihrer Anwendung ausgeführt. Sie können das C # -Objekt sogar als Argumente übergeben und es automatisieren.

Versuchen Sie es auch mit NCalc, wenn Sie nur den Ausdruck Ihrer Eigenschaften auswerten möchten .

Sébastien Ros - MSFT
quelle
3

Sie können Reflection verwenden, um die Eigenschaft abzurufen und aufzurufen. Etwas wie das:

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

Angenommen, das Objekt mit der Eigenschaft heißt "theObject" :)

David Wengier
quelle
2

Sie können auch einen Webbrowser implementieren und dann eine HTML-Datei laden, die Javascript enthält.

Dann gehen Sie document.InvokeScriptin diesem Browser zur Methode. Der Rückgabewert der eval-Funktion kann abgefangen und in alles konvertiert werden, was Sie benötigen.

Ich habe dies in mehreren Projekten gemacht und es funktioniert perfekt.

Ich hoffe es hilft

Xodem
quelle
0

Sie könnten es mit einer Prototypfunktion tun:

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

und so weiter...

GateKiller
quelle
0

Verwendet Reflection, um einen datenbindenden Ausdruck zur Laufzeit für ein Objekt zu analysieren und auszuwerten.

DataBinder.Eval-Methode

Jan Bannister
quelle
0

Ich habe ein Paket geschrieben, SharpByte.Dynamic , um das dynamische Kompilieren und Ausführen von Code zu vereinfachen. Der Code kann für jedes Kontextobjekt mit Erweiterungsmethoden aufgerufen werden, wie hier näher beschrieben .

Beispielsweise,

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

gibt 3 zurück;

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

Gibt die Zeichenfolgendarstellung des Kontextobjekts zurück.

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

führt diese Anweisungen als Skript usw. aus.

Ausführbare Dateien können einfach mit einer Factory-Methode abgerufen werden, wie im Beispiel hier gezeigt. Sie benötigen lediglich den Quellcode und die Liste aller erwarteten benannten Parameter (Token werden in dreifacher Klammer wie {{{0}} eingebettet). }, um Kollisionen mit string.Format () sowie Lenker-ähnlichen Syntaxen zu vermeiden):

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

Jedes ausführbare Objekt (Skript oder Ausdruck) ist threadsicher, kann gespeichert und wiederverwendet werden, unterstützt die Protokollierung innerhalb eines Skripts, speichert Zeitinformationen und die letzte Ausnahme, falls diese auftreten usw. Außerdem wird für jedes Objekt eine Copy () -Methode kompiliert, um dies zu ermöglichen Erstellen billiger Kopien, dh Verwenden eines ausführbaren Objekts, das aus einem Skript oder Ausdruck kompiliert wurde, als Vorlage zum Erstellen anderer.

Der Aufwand für die Ausführung eines bereits kompilierten Skripts oder einer Anweisung ist relativ gering und liegt bei bescheidener Hardware deutlich unter einer Mikrosekunde. Bereits kompilierte Skripte und Ausdrücke werden zur Wiederverwendung zwischengespeichert.

Iucounu
quelle
0

Ich habe versucht, einen Wert eines Struktur- (Klassen-) Mitglieds anhand seines Namens zu ermitteln. Die Struktur war nicht dynamisch. Alle Antworten funktionierten nicht, bis ich es endlich bekam:

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

Diese Methode gibt den Wert des Mitglieds anhand seines Namens zurück. Es funktioniert mit regulärer Struktur (Klasse).

Ilya Dan
quelle
0

Sie können die Heleonix.Reflection- Bibliothek überprüfen . Es bietet Methoden zum dynamischen Abrufen / Setzen / Aufrufen von Mitgliedern, einschließlich verschachtelter Mitglieder. Wenn ein Mitglied klar definiert ist, können Sie einen Getter / Setter (Lambda, der zu einem Delegaten kompiliert wurde) erstellen, der schneller als die Reflektion ist:

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

Wenn die Anzahl der Eigenschaften nicht unbegrenzt ist, können Sie Setter generieren und zwischenspeichern (Setter sind schneller, da sie kompilierte Delegaten sind):

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

Setter können vom Typ sein, Action<object, object>aber Instanzen können zur Laufzeit unterschiedlich sein, sodass Sie Listen von Setzern erstellen können.

Hennadii Lutsyshyn
quelle
-2

Leider verfügt C # über keine nativen Funktionen, um genau das zu tun, was Sie verlangen.

Mein C # -Eval-Programm ermöglicht jedoch die Auswertung von C # -Code. Es ermöglicht die Auswertung von C # -Code zur Laufzeit und unterstützt viele C # -Anweisungen. Tatsächlich kann dieser Code in jedem .NET-Projekt verwendet werden, ist jedoch auf die Verwendung der C # -Syntax beschränkt. Weitere Informationen finden Sie auf meiner Website http://csharp-eval.com .

xnaterm
quelle
Werfen
-9

Die richtige Antwort ist, dass Sie das gesamte Ergebnis zwischenspeichern müssen, um die Speichernutzung gering zu halten.

Ein Beispiel würde so aussehen

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

und fügen Sie es einer Liste hinzu

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

Speichern Sie die ID und verwenden Sie sie im Code

hoffe das hilft

Dachboden
quelle
5
Jemand hat die Bewertung mit der Suche verwechselt. Wenn Sie alle möglichen Programme kennen (ich denke , das ist mindestens NP-schwer) ... und Sie haben eine Supermaschine, um alle möglichen Ergebnisse vorkompilieren zu können ... und es gibt keine Nebenwirkungen / externen Eingaben ... Ja, diese Idee funktioniert theoretisch . Der Code ist jedoch ein großer Syntaxfehler
siehe