在 C# 4.0 中执行字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/760088/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 00:10:10  来源:igfitidea点击:

Execute a string in C# 4.0

c#

提问by majkinetor

I want to execute dynamically created string in C#. I know VB and JScript.Net can do it, and there is even a wayto use its assembly in C# as a workaround. I also found this articledescribing how to do it.

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

I read today about C# 4.0 features which bring it closer to the dynamic languages that have this as one of the main features. So, does anybody know does C# 4.0 includes some built in features that allows for string execution, or any other way to do whats described in the article above.

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

采纳答案by Anton Gogolev

There's no other way of executing arbitrary C# source code other than compiling it into assembly and then executing it. Anders Hejlsberg (architect of C#) announced plans to expose the C# compiler as a service (basically a set of CLR classes), so this might be of some help when this happens.

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

"Compiler as a Service" basically means that you can compile an arbitrary piece of code into Expression or, better yet, into an AST and generally get hold of internal compiler workings.

“编译器即服务”基本上意味着您可以将任意一段代码编译为 Expression 或者更好的是编译为 AST,并且通常可以掌握内部编译器的工作原理。

回答by Mehrdad Afshari

This has not much to do with dynamicfeatures of C# 4.0. Rather, the enhancements of the managed compiler and exposing its data structures to managed code makes it so easy.

这与dynamicC# 4.0 的特性没有太大关系。相反,托管编译器的增强以及将其数据结构暴露给托管代码使其变得如此简单。

回答by Marc Gravell

At the moment, the CSharpCodeProvider(in the article you cite) is the only way in the MS .NET implementation. The "compiler as a service" is one of the .NET vFuture features, and provides exactly what you ask for. Mono 2.x already has something comparable, IIRC (as discussed here).

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

回答by JeeBee

Is it essential that the language in the string be C#?

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

I know that Java can execute Python and Ruby dynamically if you include the relevant Jars, and I can't see why someone wouldn't have thought to port these systems to C# and .NET.

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

回答by Mesh

You could dynamically create a XSLT document in memory that includes a c# extension method.

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

The actual transformation could be little more then passing parms to the extension method and return results.

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

But the cited article is probably easier to use....

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

Whats the problem with using that code?

使用该代码有什么问题?

回答by Peter Wone

This is dead easy to do. I built the following convenience wrappers. They are structured so that you can construct an assembly from fragments of source code defining methods or expressions and invoke them by name using the helper methods of DynamicCodeManager.

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

The code is compiled on demand in response to invocation. Adding more methods will cause automatic recompilation on next invocation.

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

You provide only a method body. If you don't want to return a value then return null and don't bother to use the object returned by InvokeMethod.

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

If you use this in commercial code do me a favour and credit my work. The real jewel in this library is the invocation support. Getting the code to compile isn't the problem, it's invocation. It's quite tricky to get reflection to correctly match the method signature when you have a variable length parameter list. This is the reason for the existence of DynamicBase: the compiler resolves method binding to this explicitly declared base class giving us access to the right VMT. From there on in it all comes out in the wash.

如果您在商业代码中使用它,请帮我一个忙并感谢我的工作。这个库中真正的宝石是调用支持。让代码编译不是问题,而是调用。当您有一个可变长度的参数列表时,让反射正确匹配方法签名是非常棘手的。这就是 DynamicBase 存在的原因:编译器解析方法绑定到这个显式声明的基类,让我们可以访问正确的 VMT。从那里开始,它都在洗涤中出现。

I should also point out that this capability makes your desktop application vulnerable to script injection attacks. You should either take great care to vet the origin of script or reduce the trust level under which the generated assembly runs.

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

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