Esegui una stringa in C # 4.0

Voglio eseguire una stringa creata dynamicmente in C #. So che VB e JScript.Net possono farlo, e c’è anche un modo per usare il suo assembly in C # come soluzione alternativa. Ho anche trovato questo articolo che descrive come farlo.

Oggi ho letto delle caratteristiche di C # 4.0 che lo avvicinano ai linguaggi dinamici che hanno questa caratteristica come una delle principali. Quindi, qualcuno sa che C # 4.0 include alcune funzionalità incorporate che consentono l’esecuzione di stringhe, o qualsiasi altro modo per fare ciò che è descritto nell’articolo sopra.

Non c’è altro modo per eseguire codice sorgente C # arbitrario diverso dalla compilazione in assembly e quindi dall’esecuzione. Anders Hejlsberg (architetto di C #) ha annunciato piani per esporre il compilatore C # come servizio (fondamentalmente un insieme di classi CLR), quindi questo potrebbe essere di aiuto quando ciò accade.

“Compilatore come servizio” significa fondamentalmente che è ansible compilare un codice arbitrario di codice in Expression o, meglio ancora, in un AST e in genere ottenere il funzionamento interno del compilatore.

Questo è facile da fare. Ho costruito i seguenti wrapper di convenienza. Sono strutturati in modo da poter build un assembly da frammenti di codice sorgente che definiscono metodi o espressioni e richiamarli per nome utilizzando i metodi helper di DynamicCodeManager.

Il codice è compilato su richiesta in risposta all’invocazione. L’aggiunta di più metodi causerà la ricompilazione automatica al prossimo richiamo.

Fornisci solo un corpo del metodo. Se non si desidera restituire un valore, restituire null e non preoccuparsi di utilizzare l’object restituito da InvokeMethod.

Se usi questo codice commerciale, fammi un favore e concedi credito al mio lavoro. Il vero gioiello di questa libreria è il supporto per l’invocazione. Ottenere il codice da compilare non è un problema, è un’invocazione. È piuttosto difficile ottenere riflessioni per abbinare correttamente la firma del metodo quando si ha un elenco di parametri di lunghezza variabile. Questo è il motivo dell’esistenza di DynamicBase: il compilatore risolve l’associazione del metodo a questa class base dichiarata esplicitamente dandoci accesso al VMT corretto. Da lì in poi tutto viene fuori nel lavaggio.

Devo anche sottolineare che questa capacità rende l’applicazione desktop vulnerabile agli attacchi di script injection. È necessario prestare molta attenzione a controllare l’origine dello script o ridurre il livello di attendibilità in cui viene eseguito l’assembly generato.

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

Al momento, CSharpCodeProvider (nell’articolo che citi) è l’unico modo nell’implementazione di MS .NET. Il “compilatore come servizio” è una delle funzionalità di .NET vFuture e fornisce esattamente ciò che chiedi. Mono 2.x ha già qualcosa di simile, IIRC ( come discusso qui ).

Questo non ha molto a che fare con dynamic caratteristiche dynamic di C # 4.0. Piuttosto, i miglioramenti del compilatore gestito e l’esposizione delle sue strutture dati al codice gestito lo rendono così facile.

È essenziale che la lingua nella stringa sia C #?

So che Java è in grado di eseguire Python e Ruby in modo dinamico se si includono i relativi jar, e non riesco a capire perché qualcuno non avrebbe pensato di trasferire questi sistemi su C # e .NET.

È ansible creare dynamicmente un documento XSLT in memoria che includa il metodo di estensione # ac.

La trasformazione effettiva potrebbe essere un po ‘più di quella di passare i parms al metodo di estensione e restituire i risultati.

Ma l’articolo citato è probabilmente più facile da usare ….

Qual è il problema con l’utilizzo di quel codice?