Come posso valutare dinamicamente il codice C #?


92

Posso fare un eval("something()");per eseguire il codice dinamicamente in JavaScript. C'è un modo per me di fare la stessa cosa in C #?

Un esempio di quello che sto cercando di fare è: ho una variabile intera (diciamo i) e ho più proprietà con i nomi: "Proprietà1", "Proprietà2", "Proprietà3", ecc. Ora, voglio eseguire alcune operazioni sulla proprietà "Proprietà i " a seconda del valore di i.

Questo è davvero semplice con Javascript. C'è un modo per farlo con C #?



2
c # chiama eval di ironpython. L'ho provato in c # 4.0. nessuna esperienza con c # 2.0
Peter Long

@ Peter Long, dove posso trovare la documentazione sull'eval di IronPython?
smartcaveman

@AdhipGupta So che questa domanda e risposta è piuttosto datata, ma ho appena pubblicato una playlist video che suona molto simile alla descrizione data nella risposta di Davide Icardi. È radicalmente diverso e probabilmente vale la pena provarlo.
Rick Riggs

Risposte:


49

Sfortunatamente, C # non è un linguaggio dinamico come quello.

Quello che puoi fare, tuttavia, è creare un file di codice sorgente C #, pieno di classe e tutto, ed eseguirlo tramite il provider CodeDom per C # e compilarlo in un assembly, quindi eseguirlo.

Questo post del forum su MSDN contiene una risposta con un po 'di codice di esempio in fondo alla pagina:
creare un metodo anonimo da una stringa?

Difficilmente direi che questa è un'ottima soluzione, ma è comunque possibile.

Che tipo di codice ti aspetteresti in quella stringa? Se si tratta di un sottoinsieme minore di codice valido, ad esempio solo espressioni matematiche, potrebbe essere che esistano altre alternative.


Modifica : Bene, questo mi insegna a leggere prima le domande a fondo. Sì, la riflessione potrebbe darti un aiuto qui.

Se dividi la stringa per; in primo luogo, per ottenere singole proprietà, è possibile utilizzare il codice seguente per ottenere un oggetto PropertyInfo per una particolare proprietà per una classe, quindi utilizzare tale oggetto per manipolare un particolare oggetto.

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

Collegamento: metodo PropertyInfo.SetValue


cosa succede se GetProperty deve essere una funzione con x, y parametri?, significa Testo (1,2)?
user1735921

@ user1735921 Quindi dovrai GetMethod(methodName)invece usare , analizzare i valori dei parametri e chiamare il metodo usando la reflection.
Lasse V. Karlsen

typeof (ObjectType) è analogo a someObject.GetType ()
Thiago

32

Utilizzo dell'API di scripting Roslyn (ulteriori esempi qui ):

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

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

Puoi anche eseguire qualsiasi parte di codice:

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

E fai riferimento al codice che è stato generato nelle esecuzioni precedenti:

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

14

Non proprio. Puoi usare la riflessione per ottenere ciò che desideri, ma non sarà così semplice come in Javascript. Ad esempio, se si desidera impostare il campo privato di un oggetto su qualcosa, è possibile utilizzare questa funzione:

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

Questa è una funzione eval in c #. L'ho usato per convertire funzioni anonime (Lambda Expressions) da una stringa. Fonte: 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 Whoops, ho corretto l'errore di battitura (Lambada => Lambda). Non sapevo che la canzone si chiamasse Lambada, quindi questa non è stata intenzionale. ;)
Largo

Non riuscivo a capire perché questa risposta ottenga meno voti. È molto utile.
Muzaffer Galata

9

Ho scritto un progetto open source, Dynamic Expresso , che può convertire espressioni di testo scritte utilizzando una sintassi C # in delegati (o albero delle espressioni). Le espressioni vengono analizzate e trasformate in alberi delle espressioni senza utilizzare la compilazione o la riflessione.

Puoi scrivere qualcosa come:

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

o

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

Il mio lavoro è basato sull'articolo di Scott Gu http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .


7

Tutto ciò funzionerebbe sicuramente. Personalmente, per quel particolare problema, avrei probabilmente adottato un approccio leggermente diverso. Forse qualcosa del genere:

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

Quando si utilizzano modelli come questo, è necessario fare attenzione che i dati vengano memorizzati per riferimento e non per valore. In altre parole, non farlo con le primitive. Devi usare le loro grandi controparti di classe gonfie.

Mi sono reso conto che non è esattamente la domanda, ma la domanda ha avuto una risposta abbastanza buona e ho pensato che forse un approccio alternativo potrebbe aiutare.


5

Ora non lo so se si desidera assolutamente eseguire istruzioni C #, ma è già possibile eseguire istruzioni Javascript in C # 2.0. La libreria open source Jint è in grado di farlo. È un interprete Javascript per .NET. Passa un programma Javascript e verrà eseguito all'interno della tua applicazione. Puoi anche passare oggetti C # come argomenti e automatizzarli.

Inoltre, se vuoi solo valutare l'espressione sulle tue proprietà, prova NCalc .


3

Puoi usare la riflessione per ottenere la proprietà e invocarla. Qualcosa come questo:

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

Cioè, supponendo che l'oggetto che ha la proprietà si chiami "theObject" :)


2

Puoi anche implementare un browser web, quindi caricare un file html che contiene javascript.

Quindi scegli il document.InvokeScriptmetodo su questo browser. Il valore di ritorno della funzione eval può essere catturato e convertito in tutto ciò di cui hai bisogno.

L'ho fatto in diversi progetti e funziona perfettamente.

Spero che sia d'aiuto


0

Potresti farlo con una funzione prototipo:

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

e così via...



0

Ho scritto un pacchetto, SharpByte.Dynamic , per semplificare il compito di compilare ed eseguire il codice in modo dinamico. Il codice può essere richiamato su qualsiasi oggetto di contesto utilizzando i metodi di estensione come descritto in dettaglio qui .

Per esempio,

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

restituisce 3;

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

restituisce la rappresentazione di stringa dell'oggetto contesto;

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

esegue queste istruzioni come script, ecc.

Gli eseguibili possono essere ottenuti facilmente utilizzando un metodo di fabbrica, come mostrato nell'esempio qui: tutto ciò di cui hai bisogno è il codice sorgente e l'elenco di tutti i parametri denominati previsti (i token sono incorporati utilizzando la notazione a tre parentesi, come {{{0}} }, per evitare collisioni con string.Format () e sintassi simili a Handlebars):

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

Ogni oggetto eseguibile (script o espressione) è thread-safe, può essere memorizzato e riutilizzato, supporta la registrazione dall'interno di uno script, memorizza le informazioni sui tempi e l'ultima eccezione se incontrata, ecc. C'è anche un metodo Copy () compilato su ciascuno per consentire creare copie economiche, ovvero utilizzare un oggetto eseguibile compilato da uno script o un'espressione come modello per crearne altri.

Il sovraccarico di esecuzione di uno script o di un'istruzione già compilati è relativamente basso, ben al di sotto di un microsecondo su hardware modesto, e gli script e le espressioni già compilati vengono memorizzati nella cache per il riutilizzo.


0

Stavo cercando di ottenere un valore di un membro della struttura (classe) con il suo nome. La struttura non era dinamica. Tutte le risposte non hanno funzionato fino a quando non ho finalmente capito:

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

Questo metodo restituirà il valore del membro con il suo nome. Funziona su struttura regolare (classe).


0

Potresti controllare la libreria Heleonix.Reflection . Fornisce metodi per ottenere / impostare / invocare membri dinamicamente, inclusi membri annidati, oppure se un membro è chiaramente definito, è possibile creare un getter / setter (lambda compilato in un delegato) che è più veloce della riflessione:

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

O se il numero di proprietà non è infinito, puoi generare setter e chache (setter sono più veloci poiché sono delegati compilati):

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

I setter possono essere di tipo Action<object, object>ma le istanze possono essere diverse in fase di esecuzione, quindi puoi creare elenchi di setter.


-2

Sfortunatamente, C # non dispone di alcuna funzionalità nativa per fare esattamente ciò che stai chiedendo.

Tuttavia, il mio programma eval C # consente di valutare il codice C #. Fornisce la valutazione del codice C # in fase di esecuzione e supporta molte istruzioni C #. In effetti, questo codice è utilizzabile in qualsiasi progetto .NET, tuttavia è limitato all'uso della sintassi C #. Dai un'occhiata al mio sito web, http://csharp-eval.com , per ulteriori dettagli.


Dai

-9

la risposta corretta è che devi memorizzare nella cache tutti i risultati per mantenere basso l'utilizzo di mem0ry.

un esempio sarebbe simile a questo

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

e aggiungilo a un elenco

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

salva l'id e usalo nel codice

spero che questo ti aiuti


5
qualcuno ha confuso la valutazione con la ricerca. Se conosci tutti i programmi possibili ( penso che sia almeno NP-Hard) ... e hai una supermacchina per precompilare tutti i risultati possibili ... e non ci sono effetti collaterali / input esterni ... Sì, questa idea teoricamente funziona . Il codice è un grosso errore di sintassi, tuttavia
vedere il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.