在C#4.0中执行一个字符串

我想在C#中执行动态创建的字符串。 我知道VB和JScript.Net可以做到这一点,甚至有一种方法可以在C#中使用它的程序集作为解决方法。 我还发现这篇文章描述了如何做到这一点。

我今天读到了有关C#4.0function的内容,这些function使其更接近动态语言,而这些语言已成为主要function之一。 那么,是否有人知道C#4.0是否包含允许字符串执行的一些内置function,或者其他任何方式来执行上文中描述的内容。

除了将其编译成程序集然后执行它之外,没有其他方法可以执行任意C#源代码。 Anders Hejlsberg(C#的架构师)宣布计划将C#编译器公开为服务(基本上是一组CLR类),所以当发生这种情况时,这可能会有所帮助。

“编译器即服务”基本上意味着您可以将任意一段代码编译到Expression中,或者更好地编译到AST中,并且通常可以获得内部编译器工作。

这很容易做到。 我构建了以下便利包装器。 它们的结构使您可以从定义方法或表达式的源代码片段构造一个程序集,并使用DynamicCodeManager的辅助方法通过名称调用它们。

代码是根据需要编译的,以响应调用。 添加更多方法将导致下次调用时自动重新编译。

您只提供方法体。 如果您不想返回值,则返回null,并且不必费心使用InvokeMethod返回的对象。

如果你在商业代码中使用它,帮我一个忙,并相信我的工作。 这个库中真正的gem是调用支持。 获取编译代码不是问题,而是调用。 当你有一个可变长度参数列表时,让reflection正确匹配方法签名是非常棘手的。 这就是DynamicBase存在的原因:编译器解析了与此显式声明的基类的方法绑定,使我们能够访问正确的VMT。 从那里开始,所有这些都在洗涤中出现。

我还应该指出,此function使您的桌面应用程序容易受到脚本注入攻击。 您应该非常小心地审查脚本的来源或降低生成的程序集运行的信任级别。

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

目前, CSharpCodeProvider (在您引用的文章中)是MS .NET实现中的唯一方法。 “编译器即服务”是.NET vFuturefunction之一,可以提供您所要求的function。 Mono 2.x已经有了类似的东西,IIRC( 如这里所讨论的 )。

这与C#4.0的dynamicfunction没什么关系。 相反,托管编译器的增强function以​​及将其数据结构暴露给托管代码使其变得如此简单。

字符串中的语言是C#必不可少吗?

我知道如果你包含相关的Jars,Java可以动态地执行Python和Ruby,我不明白为什么有人不会想到将这些系统移植到C#和.NET。

您可以在内存中动态创建包含ac#extension方法的XSLT文档。

实际的转换可能只是将parms传递给扩展方法并返回结果。

但引用的文章可能更容易使用….

使用该代码有什么问题?