Pregunta

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

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

Esto es realmente sencillo con Javascript.¿Hay alguna manera de hacer esto con C#?

¿Fue útil?

Solución

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

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

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

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

¿Qué tipo de código esperarías 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 detenidamente 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 de una clase y luego usar ese objeto para manipular un objeto particular.

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

Enlace: Método PropertyInfo.SetValue

Otros consejos

Usando la API de secuencias de comandos de Roslyn (más muestras aquí):

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

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

También puedes 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();");

No precisamente.Puedes usar la reflexión para lograr lo que deseas, pero no será tan simple como en Javascript.Por ejemplo, si quisieras establecer algo en el campo privado de un objeto, podrías 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);
}

Esta es una función de evaluación en C#.Lo usé para convertir funciones anónimas (expresiones Lambda) a partir 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;

}

He escrito un proyecto de código abierto, expreso dinámico, 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 compilación o 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 .

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 utilizar 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 utilizar sus grandes contrapartes de clase infladas.

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.

Ahora no sé si realmente desea ejecutar declaraciones de C#, pero ya puede ejecutar declaraciones de Javascript en C# 2.0.La biblioteca de código abierto Jint es capaz de hacerlo.Es un intérprete de Javascript para .NET.Pase un programa Javascript y se ejecutará dentro de su aplicación.Incluso puedes pasar un objeto C# como argumentos y automatizarlo.

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

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, suponiendo que el objeto que tiene la propiedad se llama "theObject" :)

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

Entonces vas por el document.InvokeScript Método en este navegador.El valor de retorno de la función de evaluación se puede capturar y convertir en todo lo que necesite.

Hice esto en varios proyectos y funciona perfectamente.

Espero eso ayude

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

etcétera...

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

Método DataBinder.Eval

He escrito un paquete, SharpByte.Dinámico, 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 adelante. 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 utilizando notación de triple corchete, como {{{0}}}, para evitar colisiones con string.Format() así como con los manillares- como sintaxis):

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 la creación de copias económicas, es decir.usar un objeto ejecutable compilado a partir de un script o expresión como plantilla para crear otros.

Los gastos generales de ejecución de un script o una declaración ya compilados son relativamente bajos, muy por debajo de un microsegundo en hardware modesto, y los scripts y expresiones ya compilados se almacenan en caché para su reutilización.

Estaba intentando obtener el valor de un miembro de la 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).

Podrías comprobar el Heleonix.Reflexión biblioteca.Proporciona métodos para obtener/establecer/invocar miembros dinámicamente, incluidos miembros anidados, o si un miembro está claramente definido, puede crear un captador/definidor (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 definidores y almacenarlos en caché (los definidores 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 configuradores pueden ser de tipo Action<object, object> pero las instancias pueden ser diferentes en tiempo de ejecución, por lo que puedes crear listas de configuradores.

Desafortunadamente, C# no tiene funciones nativas para hacer exactamente lo que usted solicita.

Sin embargo, mi programa de evaluación de C# permite evaluar el código de 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, está limitado al uso de la sintaxis de C#.Echa un vistazo a mi sitio web, http://csharp-eval.com, para obtener detalles adicionales.

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

guarde la identificación y úsela en el código

espero que esto ayude

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top