代码之家  ›  专栏  ›  技术社区  ›  majkinetor

在C 4.0中执行字符串

c#
  •  6
  • majkinetor  · 技术社区  · 15 年前

    我想在C中执行动态创建的字符串。我知道vb和jscript.net可以做到,甚至还有 way 在C中使用其组件作为解决方案。我也发现了这个 article 描述如何做。

    我今天读到了关于C 4.0特性的文章,这些特性使它更接近于将它作为主要特性之一的动态语言。所以,是否有人知道C 4.0包含了一些允许字符串执行的内置功能,或者任何其他方式来完成上面文章中描述的工作。

    6 回复  |  直到 9 年前
        1
  •  11
  •   Anton Gogolev    15 年前

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

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

        2
  •  13
  •   Peter Wone    15 年前

    这太容易了。我做了以下便利包装。它们的结构使您可以从定义方法或表达式的源代码片段构造程序集,并使用DynamicDemanager的帮助方法按名称调用它们。

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

    您只提供一个方法体。如果您不想返回一个值,那么返回空值,并且不需要使用invokeMethod返回的对象。

    如果你在商业代码中使用这个,请帮我一个忙,并赞扬我的工作。这个库中真正的宝石是调用支持。获取要编译的代码不是问题,而是调用。当您有一个可变长度的参数列表时,要让反射正确匹配方法签名是非常困难的。这就是存在dynamicbase的原因:编译器解析此显式声明的基类的方法绑定,使我们能够访问正确的vmt。从那里开始,所有的东西都洗出来了。

    我还应该指出,这种功能使您的桌面应用程序容易受到脚本注入攻击。您应该非常注意检查脚本的源代码,或者降低生成的程序集运行时所处的信任级别。

    动态基础

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

    动态需求管理器.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<string, string> _conditionSnippet = new Dictionary<string, string>();
        static Dictionary<string, string> _methodSnippet = new Dictionary<string, string>();
        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<string> _references = new List<string>("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<string, string> kvp in _conditionSnippet)
              src.AppendFormat(ConditionTemplate, DynamicConditionPrefix, kvp.Key, kvp.Value);
            foreach (KeyValuePair<string, string> 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);
        }
      }
    }
    
        3
  •  4
  •   Marc Gravell    15 年前

    目前, CSharpCodeProvider (在您引用的文章中)是MS.NET实现的唯一方法。“编译器即服务”是.NET vFuture的一个功能,它提供了您所要求的内容。Mono2.x已经有了类似的功能,IIRC( as discussed here )

        4
  •  1
  •   Mehrdad Afshari    15 年前

    这与 dynamic C 4.0的特点。相反,托管编译器的增强功能以及将其数据结构公开给托管代码使其变得如此简单。

        5
  •  0
  •   JeeBee    15 年前

    字符串中的语言是否必须是C?

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

        6
  •  0
  •   Mesh    15 年前

    您可以在内存中动态创建一个包含C扩展方法的XSLT文档。

    实际的转换可能比将parm传递到扩展方法并返回结果要简单得多。

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

    使用该代码有什么问题?