Ejecuta una cadena en C # 4.0

Quiero ejecutar cadena creada dinámicamente en C #. Sé que VB y JScript.Net pueden hacerlo, e incluso hay una manera de usar su ensamblaje en C # como una solución alternativa. También encontré este artículo que describe cómo hacerlo.

Hoy leí acerca de las características de C # 4.0 que lo acercan a los lenguajes dynamics que tienen esto como una de las características principales. Entonces, ¿alguien sabe que C # 4.0 incluye algunas características integradas que permiten la ejecución de cadenas o cualquier otra forma de hacer lo que se describe en el artículo anterior?

No hay otra forma de ejecutar código fuente C # arbitrario que no sea comstackrlo en ensamblaje y luego ejecutarlo. Anders Hejlsberg (arquitecto de C #) anunció planes para exponer el comstackdor de C # como un servicio (básicamente un conjunto de clases de CLR), por lo que podría ser de alguna ayuda cuando esto ocurra.

El “comstackdor como servicio” básicamente significa que puede comstackr un código arbitrario en Expression o, mejor aún, en AST y, en general, obtener el funcionamiento del comstackdor interno.

Esto es muy fácil de hacer. Creé las siguientes envolturas de conveniencia. Están estructurados para que pueda construir un ensamblaje a partir de fragmentos de código fuente que definen métodos o expresiones e invocarlos por nombre utilizando los métodos auxiliares de DynamicCodeManager.

El código se comstack a pedido en respuesta a la invocación. Agregar más métodos provocará la recomstackción automática en la próxima invocación.

Usted proporciona solo un cuerpo de método. Si no desea devolver un valor, devuelva nulo y no se moleste en usar el objeto devuelto por InvokeMethod.

Si usas esto en el código comercial, hazme un favor y acredita mi trabajo. La verdadera joya de esta biblioteca es el soporte de invocación. Obtener el código para comstackr no es el problema, es una invocación. Es bastante difícil lograr que la reflexión coincida correctamente con la firma del método cuando se tiene una lista de parámetros de longitud variable. Esta es la razón de la existencia de DynamicBase: el comstackdor resuelve el enlace del método a esta clase base explícitamente declarada que nos da acceso al VMT correcto. A partir de ahí, todo sale en el lavado.

También debo señalar que esta capacidad hace que su aplicación de escritorio sea vulnerable a los ataques de inyección de scripts. Debe tener especial cuidado en investigar el origen del script o reducir el nivel de confianza bajo el cual se ejecuta el ensamblado generado.

DynamicBase.cs

using System.Reflection; namespace Dynamo { public abstract class DynamicBase { public bool EvaluateCondition(string methodName, params object[] p) { methodName = string.Format("__dm_{0}", methodName); BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic; return (bool)GetType().InvokeMember(methodName, flags, null, this, p); } public object InvokeMethod(string methodName, params object[] p) { BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic; return GetType().InvokeMember(methodName, flags, null, this, p); } public double Transform(string functionName, params object[] p) { functionName = string.Format("__dm_{0}", functionName); BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic; return (double)GetType().InvokeMember(functionName, flags, null, this, p); } } } 

DynamicCodeManager.cs

 using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Text; using Microsoft.CSharp; namespace Dynamo { public static class DynamicCodeManager { #region internal statics and constants static Dictionary _conditionSnippet = new Dictionary(); static Dictionary _methodSnippet = new Dictionary(); static string CodeStart = "using System;\r\nusing System.Collections.Generic;\r\n//using System.Linq;\r\nusing System.Text;\r\nusing System.Data;\r\nusing System.Reflection;\r\nusing System.CodeDom.Compiler;\r\nusing Microsoft.CSharp;\r\nnamespace Dynamo\r\n{\r\n public class Dynamic : DynamicBase\r\n {\r\n"; static string DynamicConditionPrefix = "__dm_"; static string ConditionTemplate = " bool {0}{1}(params object[] p) {{ return {2}; }}\r\n"; static string MethodTemplate = " object {0}(params object[] p) {{\r\n{1}\r\n }}\r\n"; static string CodeEnd = " }\r\n}"; static List _references = new List("System.dll,System.dll,System.Data.dll,System.Xml.dll,mscorlib.dll,System.Windows.Forms.dll".Split(new char[] { ',' })); static Assembly _assembly = null; #endregion public static Assembly Assembly { get { return DynamicCodeManager._assembly; } } #region manage snippets public static void Clear() { _methodSnippet.Clear(); _conditionSnippet.Clear(); _assembly = null; } public static void Clear(string name) { if (_conditionSnippet.ContainsKey(name)) { _assembly = null; _conditionSnippet.Remove(name); } else if (_methodSnippet.ContainsKey(name)) { _assembly = null; _methodSnippet.Remove(name); } } public static void AddCondition(string conditionName, string booleanExpression) { if (_conditionSnippet.ContainsKey(conditionName)) throw new InvalidOperationException(string.Format("There is already a condition called '{0}'", conditionName)); StringBuilder src = new StringBuilder(CodeStart); src.AppendFormat(ConditionTemplate, DynamicConditionPrefix, conditionName, booleanExpression); src.Append(CodeEnd); Compile(src.ToString()); //if the condition is invalid an exception will occur here _conditionSnippet[conditionName] = booleanExpression; _assembly = null; } public static void AddMethod(string methodName, string methodSource) { if (_methodSnippet.ContainsKey(methodName)) throw new InvalidOperationException(string.Format("There is already a method called '{0}'", methodName)); if (methodName.StartsWith(DynamicConditionPrefix)) throw new InvalidOperationException(string.Format("'{0}' is not a valid method name because the '{1}' prefix is reserved for internal use with conditions", methodName, DynamicConditionPrefix)); StringBuilder src = new StringBuilder(CodeStart); src.AppendFormat(MethodTemplate, methodName, methodSource); src.Append(CodeEnd); Trace.TraceError("SOURCE\r\n{0}", src); Compile(src.ToString()); //if the condition is invalid an exception will occur here _methodSnippet[methodName] = methodSource; _assembly = null; } #endregion #region use snippets public static object InvokeMethod(string methodName, params object[] p) { DynamicBase _dynamicMethod = null; if (_assembly == null) { Compile(); _dynamicMethod = _assembly.CreateInstance("Dynamo.Dynamic") as DynamicBase; } return _dynamicMethod.InvokeMethod(methodName, p); } public static bool Evaluate(string conditionName, params object[] p) { DynamicBase _dynamicCondition = null; if (_assembly == null) { Compile(); _dynamicCondition = _assembly.CreateInstance("Dynamo.Dynamic") as DynamicBase; } return _dynamicCondition.EvaluateCondition(conditionName, p); } public static double Transform(string functionName, params object[] p) { DynamicBase _dynamicCondition = null; if (_assembly == null) { Compile(); _dynamicCondition = _assembly.CreateInstance("Dynamo.Dynamic") as DynamicBase; } return _dynamicCondition.Transform(functionName, p); } #endregion #region support routines public static string ProduceConditionName(Guid conditionId) { StringBuilder cn = new StringBuilder(); foreach (char c in conditionId.ToString().ToCharArray()) if (char.IsLetterOrDigit(c)) cn.Append(c); string conditionName = cn.ToString(); return string.Format("_dm_{0}",cn); } private static void Compile() { if (_assembly == null) { StringBuilder src = new StringBuilder(CodeStart); foreach (KeyValuePair kvp in _conditionSnippet) src.AppendFormat(ConditionTemplate, DynamicConditionPrefix, kvp.Key, kvp.Value); foreach (KeyValuePair kvp in _methodSnippet) src.AppendFormat(MethodTemplate, kvp.Key, kvp.Value); src.Append(CodeEnd); Trace.TraceError("SOURCE\r\n{0}", src); _assembly = Compile(src.ToString()); } } private static Assembly Compile(string sourceCode) { CompilerParameters cp = new CompilerParameters(); cp.ReferencedAssemblies.AddRange(_references.ToArray()); cp.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName); cp.CompilerOptions = "/target:library /optimize"; cp.GenerateExecutable = false; cp.GenerateInMemory = true; CompilerResults cr = (new CSharpCodeProvider()).CompileAssemblyFromSource(cp, sourceCode); if (cr.Errors.Count > 0) throw new CompilerException(cr.Errors); return cr.CompiledAssembly; } #endregion public static bool HasItem(string methodName) { return _conditionSnippet.ContainsKey(methodName) || _methodSnippet.ContainsKey(methodName); } } } 

Por el momento, CSharpCodeProvider (en el artículo que cita) es la única forma en la implementación de MS .NET. El “comstackdor como servicio” es una de las funciones .NET vFuture y proporciona exactamente lo que usted solicita. Mono 2.x ya tiene algo comparable, IIRC ( como se discute aquí ).

Esto no tiene mucho que ver con dynamic características dynamic de C # 4.0. Por el contrario, las mejoras del comstackdor gestionado y la exposición de sus estructuras de datos al código administrado lo hacen tan fácil.

¿Es esencial que el lenguaje de la cadena sea C #?

Sé que Java puede ejecutar Python y Ruby dinámicamente si incluye los Jars relevantes, y no veo por qué alguien no hubiera pensado en portar estos sistemas a C # y .NET.

Puede crear dinámicamente un documento XSLT en memoria que incluya el método de extensión ac #.

La transformación real podría ser un poco más que pasar parms al método de extensión y devolver resultados.

Pero el artículo citado es probablemente más fácil de usar …

¿Cuál es el problema con el uso de ese código?