Domanda

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

Un esempio di ciò 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 sul file " Propertyio "immobile a seconda del valore di i.

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

È stato utile?

Soluzione

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

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

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

Difficilmente direi che questa sia 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, è possibile che esistano altre alternative.


Modificare:Bene, questo mi insegna a leggere prima attentamente le domande.Sì, la riflessione potrebbe darti qualche aiuto qui.

Se dividi la stringa con ;innanzitutto, per ottenere proprietà individuali, è possibile utilizzare il codice seguente per ottenere un oggetto PropertyInfo per una particolare proprietà di una classe, quindi utilizzare quell'oggetto per manipolare un oggetto particolare.

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

Collegamento: Metodo PropertyInfo.SetValue

Altri suggerimenti

Utilizzando l'API di scripting Roslyn (altro campioni 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 pezzo di codice:

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

E fai riferimento al codice generato nelle esecuzioni precedenti:

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

Non proprio.Puoi utilizzare la riflessione per ottenere ciò che desideri, ma non sarà così semplice come in Javascript.Ad esempio, se volessi impostare il campo privato di un oggetto su qualcosa, potresti 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);
}

Questa è una funzione di valutazione in C#.L'ho usato per convertire funzioni anonime (espressioni Lambda) 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;

}

Ho scritto un progetto open source, Espresso dinamico, che può convertire un'espressione di testo scritta utilizzando una sintassi C# in delegati (o albero delle espressioni).Le espressioni vengono analizzate e trasformate in Alberi di espressione 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 .

Tutto ciò funzionerebbe sicuramente.Personalmente, per quel particolare problema, probabilmente adotterei 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 usi modelli come questo, devi fare attenzione che i tuoi dati siano archiviati 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 questa la domanda, ma la domanda ha ricevuto una risposta abbastanza chiara e ho pensato che forse un approccio alternativo potrebbe essere d'aiuto.

Non lo so ora se vuoi assolutamente eseguire istruzioni C#, ma puoi già 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 ed eseguirne l'automazione.

Inoltre, se vuoi solo valutare l'espressione delle tue proprietà, provalo NCalc.

È possibile utilizzare la riflessione per ottenere la proprietà e invocarla.Qualcosa come questo:

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

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

Potresti anche implementare un browser Web, quindi caricare un file html che contiene javascript.

Allora vai per il document.InvokeScript Metodo su questo browser.Il valore restituito dalla 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

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

Utilizza la riflessione per analizzare e valutare un'espressione di associazione dati rispetto a un oggetto in fase di esecuzione.

Metodo DataBinder.Eval

Ho scritto un pacchetto, SharpByte.Dynamic, per semplificare l'attività di compilazione ed esecuzione del codice in modo dinamico.Il codice può essere richiamato su qualsiasi oggetto di contesto utilizzando metodi di estensione come spiegato più avanti Qui.

Per esempio,

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

restituisce 3;

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

restituisce la rappresentazione della stringa dell'oggetto contesto;

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

esegue quelle istruzioni come uno script, ecc.

Gli eseguibili possono essere ottenuti facilmente utilizzando un metodo factory, 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 parentesi triple, come {{{0}}}, per evitare collisioni con string.Format() e Handlebars- come le sintassi):

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

Ogni oggetto eseguibile (script o espressione) è thread-safe, può essere archiviato e riutilizzato, supporta la registrazione dall'interno di uno script, memorizza informazioni temporali e l'ultima eccezione se incontrata, ecc.Esiste anche un metodo Copy() compilato su ciascuno per consentire la creazione di copie economiche, ad es.utilizzando un oggetto eseguibile compilato da uno script o un'espressione come modello per crearne altri.

Il sovraccarico per l'esecuzione di uno script o di un'istruzione già compilata è 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.

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 finché non l'ho finalmente ottenuta:

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

Questo metodo restituirà il valore del membro in base al suo nome.Funziona su una struttura regolare (classe).

Potresti controllare il Heleonix.Riflessione biblioteca.Fornisce metodi per ottenere/impostare/invocare membri in modo dinamico, inclusi membri nidificati o, 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);

Oppure se il numero di proprietà non è infinito, puoi generare setter e memorizzarli nella cache (i 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.

Sfortunatamente, C# non dispone di funzionalità native per fare esattamente ciò che chiedi.

Tuttavia, il mio programma di valutazione 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 all'interno di qualsiasi progetto .NET, tuttavia è limitato all'uso della sintassi C#.Dai un'occhiata al mio sito web, http://csharp-eval.com, per ulteriori dettagli.

la risposta corretta è che è necessario memorizzare nella cache tutti i risultati per mantenere basso l'utilizzo della memoria.

un esempio sarebbe simile a questo

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

e aggiungerlo 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

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