¿Cómo puedo evaluar el código C # de forma dinámica?

92

Puedo hacer un eval("something()"); para ejecutar el código dinámicamente en JavaScript. ¿Hay alguna forma de hacer lo mismo en C #?

Un ejemplo de lo que estoy tratando de hacer es: tengo una variable entera (digamos i) y tengo varias propiedades por los nombres: "Propiedad1", "Propiedad2", "Propiedad3", etc. Ahora, quiero realizar algunas operaciones en la "Propiedad i " dependiendo del valor de i.

Esto es realmente simple con Javascript. ¿Hay alguna forma de hacer esto con C #?

Adhip Gupta
fuente
Eche un vistazo al shell interactivo CSharp de Mono . Tiene funciones similares a eval .
Igor Brejc
2
c # llamar a eval de ironpython. Lo probé en c # 4.0. sin experiencia con c # 2.0
Peter Long
@Peter Long, ¿dónde puedo encontrar documentación sobre la evaluación de IronPython?
smartcaveman
@AdhipGupta Sé que esta sesión de preguntas y respuestas está bastante anticuada, pero acabo de lanzar una lista de reproducción de videos que se parece mucho a la descripción que se da en la respuesta de Davide Icardi. Es dramáticamente diferente y probablemente valga la pena echarle un vistazo.
Rick Riggs

Respuestas:

49

Desafortunadamente, C # no es un lenguaje dinámico como ese.

Lo que puede hacer, sin embargo, es crear un archivo de código fuente C #, lleno de clase y todo, y ejecutarlo a través del proveedor CodeDom para C # y compilarlo en un ensamblado, y luego ejecutarlo.

Esta publicación del foro en MSDN contiene una respuesta con algún código de ejemplo en la página: ¿
crear un método anónimo a partir de una cadena?

Difícilmente diría que esta es una muy buena solución, pero es posible de todos modos.

¿Qué tipo de código espera en esa cadena? Si se trata de un subconjunto menor de código válido, por ejemplo, solo expresiones matemáticas, es posible que existan otras alternativas.


Editar : Bueno, eso me enseña a leer las preguntas a fondo primero. Sí, la reflexión podría ayudarte aquí.

Si divide la cadena por; Primero, para obtener propiedades individuales, puede usar el siguiente código para obtener un objeto PropertyInfo para una propiedad particular para una clase, y luego usar ese objeto para manipular un objeto en particular.

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

Enlace: Método PropertyInfo.SetValue

Lasse V. Karlsen
fuente
¿Qué pasa si GetProperty tiene que ser una función con x, y params ?, significa Text (1,2)?
user1735921
@ user1735921 Entonces necesitará usar GetMethod(methodName)en su lugar, analizar los valores de los parámetros y llamar al método usando la reflexión.
Lasse V. Karlsen
typeof (ObjectType) es análogo a someObject.GetType ()
Thiago
34

Usando la API de scripting de Roslyn (más ejemplos aquí ):

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

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

También puede ejecutar cualquier fragmento de código:

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

Y haga referencia al código que se generó en ejecuciones anteriores:

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

Realmente no. Puede usar la reflexión para lograr lo que desea, pero no será tan simple como en Javascript. Por ejemplo, si desea establecer el campo privado de un objeto en algo, puede usar esta función:

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
fuente
11

Esta es una función eval en c #. Lo usé para convertir funciones anónimas (Expresiones Lambda) de una cadena. Fuente: 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
fuente
1
@sehe Vaya, he corregido el error tipográfico (Lambada => Lambda). No sabía que la canción se llama Lambada, así que esta no fue intencional. ;)
Largo
No pude entender por qué esta respuesta obtuvo menos votos. Es muy útil.
Muzaffer Galata
9

Escribí un proyecto de código abierto, Dynamic Expresso , que puede convertir expresiones de texto escritas con una sintaxis de C # en delegados (o árbol de expresiones). Las expresiones se analizan y transforman en árboles de expresión sin utilizar la compilación o la reflexión.

Puedes escribir algo como:

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

Mi trabajo se basa en el artículo de Scott Gu http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .

Davide Icardi
fuente
7

Todo eso definitivamente funcionaría. Personalmente, para ese problema en particular, probablemente adoptaría un enfoque un poco diferente. Quizás algo como esto:

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

Al usar patrones como este, debe tener cuidado de que sus datos se almacenen por referencia y no por valor. En otras palabras, no hagas esto con primitivas. Tienes que usar sus grandes contrapartes de clase hinchadas.

Me di cuenta de que esa no es exactamente la pregunta, pero la pregunta ha sido bastante bien respondida y pensé que tal vez un enfoque alternativo podría ayudar.

MojoFilter
fuente
5

No lo hago ahora si absolutamente desea ejecutar declaraciones de C #, pero ya puede ejecutar declaraciones de Javascript en C # 2.0. La biblioteca de código abierto Jint puede hacerlo. Es un intérprete de Javascript para .NET. Pase un programa Javascript y se ejecutará dentro de su aplicación. Incluso puede pasar el objeto C # como argumentos y automatizarlo.

Además, si solo desea evaluar la expresión en sus propiedades, pruebe NCalc .

Sébastien Ros - MSFT
fuente
3

Puede utilizar la reflexión para obtener la propiedad e invocarla. Algo como esto:

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

Es decir, asumiendo que el objeto que tiene la propiedad se llama "theObject" :)

David Wengier
fuente
2

También puede implementar un navegador web y luego cargar un archivo html que contenga javascript.

Luego opta por el document.InvokeScriptMétodo en este navegador. El valor de retorno de la función eval se puede capturar y convertir en todo lo que necesita.

Hice esto en varios proyectos y funciona perfectamente.

Espero eso ayude

Xodem
fuente
0

Podrías hacerlo con una función de 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);
}

y así...

GateKiller
fuente
0

Utiliza la reflexión para analizar y evaluar una expresión de enlace de datos con un objeto en tiempo de ejecución.

Método DataBinder.Eval

Jan Bannister
fuente
0

Escribí un paquete, SharpByte.Dynamic , para simplificar la tarea de compilar y ejecutar código dinámicamente. El código se puede invocar en cualquier objeto de contexto utilizando métodos de extensión como se detalla más aquí .

Por ejemplo,

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

devuelve 3;

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

devuelve la representación de cadena del objeto de contexto;

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

ejecuta esas declaraciones como un script, etc.

Los ejecutables se pueden obtener fácilmente usando un método de fábrica, como se ve en el ejemplo aquí; todo lo que necesita es el código fuente y la lista de los parámetros con nombre esperados (los tokens se incrustan usando la notación de tres corchetes, como {{{0}} }, para evitar colisiones con string.Format () así como sintaxis similares a Handlebars):

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

Cada objeto ejecutable (script o expresión) es seguro para subprocesos, se puede almacenar y reutilizar, admite el registro desde un script, almacena información de tiempo y la última excepción si se encuentra, etc. También hay un método Copy () compilado en cada uno para permitir crear copias baratas, es decir, utilizar un objeto ejecutable compilado a partir de un script o expresión como plantilla para crear otros.

La sobrecarga de ejecutar una secuencia de comandos o una declaración ya compilada es relativamente baja, muy por debajo de un microsegundo en un hardware modesto, y las secuencias de comandos y expresiones ya compiladas se almacenan en caché para su reutilización.

Iucounu
fuente
0

Estaba tratando de obtener un valor de un miembro de estructura (clase) por su nombre. La estructura no era dinámica. Todas las respuestas no funcionaron hasta que finalmente las obtuve:

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

Este método devolverá el valor del miembro por su nombre. Funciona en estructura regular (clase).

Ilya Dan
fuente
0

Puede consultar la biblioteca Heleonix.Reflection . Proporciona métodos para obtener / establecer / invocar miembros dinámicamente, incluidos miembros anidados, o si un miembro está claramente definido, puede crear un getter / setter (lambda compilado en un delegado) que es más rápido que la reflexión:

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

O si el número de propiedades no es infinito, puede generar establecedores y modificarlos (los establecedores son más rápidos ya que son delegados compilados):

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

Los establecedores pueden ser de tipo, Action<object, object>pero las instancias pueden ser diferentes en tiempo de ejecución, por lo que puede crear listas de establecedores.

Hennadii Lutsyshyn
fuente
-1

Desafortunadamente, C # no tiene instalaciones nativas para hacer exactamente lo que está pidiendo.

Sin embargo, mi programa de evaluación C # permite evaluar el código C #. Proporciona la evaluación del código C # en tiempo de ejecución y admite muchas declaraciones de C #. De hecho, este código se puede utilizar en cualquier proyecto .NET, sin embargo, se limita a utilizar la sintaxis de C #. Eche un vistazo a mi sitio web, http://csharp-eval.com , para obtener detalles adicionales.

xnaterm
fuente
Eche un vistazo a Roslyn Scripting API
AlexMelw
-9

la respuesta correcta es que necesita almacenar en caché todos los resultados para mantener bajo el uso de la memoria.

un ejemplo se vería así

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

y agregarlo a una lista

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

guarda la identificación y úsala en el código

espero que esto ayude

roft
fuente
5
alguien confundió la evaluación con la búsqueda. Si conoce todos los programas posibles ( creo que es al menos NP-Hard) ... y tiene una supermáquina para precompilar todos los resultados posibles ... y no hay efectos secundarios / entradas externas ... Sí, esta idea teóricamente funciona . Sin embargo, el código es un gran error de sintaxis
consulte el