2008-08-07 19 views
73

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

Lo que estoy tratando de hacer exactamente es que tengo una variable entera (por ejemplo i) y tengo varias propiedades de los nombres: "Propiedad1", "Propiedad2", "Propiedad3", etc. Ahora, quiero para realizar algunas operaciones en la propiedad "Propiedad i" según el valor de i.

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

+2

C# llame al eval de ironpython. Lo intenté en C# 4.0. ninguna experiencia con C# 2.0 –

+0

@Peter Long, ¿dónde puedo encontrar documentación sobre la evaluación de IronPython? – smartcaveman

+0

Eche un vistazo a [shell interactivo CSharp de Mono] (http://www.mono-project.com/CsharpRepl). Tiene [funciones tipo eval] (http://www.go-mono.com/docs/index.aspx?link=N:Mono.CSharp). –

Respuesta

40

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

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

Este mensaje del foro en MSDN contiene una respuesta con un código de ejemplo abajo de la página un poco:
create a anonymous method from a string?

casi no diría que esto es una muy buena solución, pero es posible de todos modos.

¿Qué tipo de código esperas 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, que me enseña a leer las preguntas a fondo en primer lugar. 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 en 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: PropertyInfo.SetValue Method

2

Puede usar el reflejo 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" :)

0

Usted podría hacerlo con un prototipo de la función:

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

14

No realmente. Puede usar el reflejo para lograr lo que quiere, pero no será tan simple como en Javascript. Por ejemplo, si desea establecer el campo privado de un objeto a algo, puede utilizar 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); 
} 
7

Todo eso sin duda trabajar. Personalmente, para ese problema en particular, probablemente tomaría un enfoque diferente.Tal vez 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, hay que tener cuidado de que sus datos están almacenados por referencia y no por valor. En otras palabras, no hagas esto con primitivos. Tienes que usar sus contrapartes de la clase hinchada grandes.

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

5

No quiero ahora si quieres ejecutar sentencias C# absolutamente, pero ya puedes ejecutar sentencias 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 puede pasar el objeto C# como argumentos y automatizarlo.

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

1

También podría implementar un Webbrowser, luego cargar un archivo html que contenga javascript.

Luego, vaya al método document.InvokeScript en este navegador. El valor de retorno de la función eval se puede atrapar y convertir en todo lo que necesita.

Lo hice en varios proyectos y funciona perfectamente.

creo que sirve

-1

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

Sin embargo, mi programa C# eval no permite evaluar el código C#. Proporciona la evaluación del código C# en el tiempo de ejecución y es compatible con muchas sentencias C#. De hecho, este código se puede utilizar en cualquier proyecto .NET, sin embargo, está limitado a usar la sintaxis C#. Echa un vistazo a mi sitio web, http://csharp-eval.com, para obtener más detalles.

+0

Eche un vistazo a [Roslyn Scripting API] (https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples) –

7

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

} 
+0

@sehe Vaya, corrigí el error tipográfico (Lambada => Lambda). No sabía que la canción se llama Lambada, así que esta fue involuntaria. ;) – Largo

-7

la respuesta correcta es que necesita para almacenar en caché todo el resultado de mantener la baja utilización mem0ry.

un ejemplo sería el siguiente

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

y añadirlo a una lista

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

guardar el ID y utilizarlo en el código

esperanza esto ayuda

+0

Uhmmm ...¿Qué quieres decir? – xfix

+3

alguien confunde la evaluación con la búsqueda. Si conoce todos los programas posibles (creo ** que es _al menos_ NP-difícil) ... y tiene una supermáquina para precompilar todos los resultados posibles ... ** y ** no hay efectos secundarios/entradas externas. .. Sí, esta idea _teóricamente_ funciona. El código es un gran error de sintaxis, aunque – sehe

0

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

DataBinder.Eval Method

+0

¿Puedes mostrar un ejemplo de cómo? –

7

He escrito un proyecto de código abierto, Dynamic Expresso, que puede convertir la expresión texto escrito utilizando una sintaxis de C# en los delegados (o árbol de expresión).Las expresiones se analizan y se transforman en Expression Trees sin usar compilación o reflexión.

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

6

Uso de la API de secuencias de comandos Roslyn (más samples here):

// 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 pieza de código:

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

y hacer referencia al código que se generó en carreras anteriores:

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

He escrito un paquete, SharpByte.Dynamic, para simplificar la tarea o f compilando y ejecutando 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 here.

Por ejemplo,

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

retornos 3;

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

devuelve la representación de cadena del objeto contextual;

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

ejecuta esas declaraciones como una secuencia de comandos, etc.

ejecutables se pueden conseguir fácilmente utilizando un método de fábrica, como se ve en el ejemplo here --todos lo que necesita es el código fuente y la lista de todas las previsiones parámetros con nombre (tokens están incrustadas usando la notación de triple soporte, como {{{0}}}, para evitar colisiones con string.Format(), así como sintaxis Manillares-como):

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

Cada objeto ejecutable (script o expresión) es thread-safe, se puede almacenar d y reutilizado, admite el registro desde un script, almacena información de tiempo y ú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 baratas, es decir, utilizando un objeto ejecutable compilado a partir de un script o expresión como una plantilla para crear otros.

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

Cuestiones relacionadas