【问题标题】:Create anonymous method from a string in c#在 C# 中从字符串创建匿名方法
【发布时间】:2009-10-15 20:31:15
【问题描述】:

是否可以在 c# 中从字符串创建匿名方法?

例如如果我有一个字符串"x + y * z",是否可以将其转换为某种方法/lambda 对象,我可以使用任意xyz 参数调用它?

【问题讨论】:

  • 我很确定微软的某个人做了一个关于 .net 4 的广播,它可以将编译器作为服务来完成这样的事情。不过不知道是不是你的情况。
  • Anders 在 PDC 谈论“编译器即服务”时谈论的是非常遥远的工作。 C# 4 肯定不会有这样的工作。
  • 重复:stackoverflow.com/questions/1437964/… 这个问题的回答也比这个问题更具体。

标签: c# anonymous-methods


【解决方案1】:

有可能,是的。您必须解析字符串,例如,使用表达式树编译委托。

以下是使用表达式树创建(x, y, z) => x + y * z 的示例:

ParameterExpression parameterX = Expression.Parameter(typeof(int), "x");
ParameterExpression parameterY = Expression.Parameter(typeof(int), "y");
ParameterExpression parameterZ = Expression.Parameter(typeof(int), "z");
Expression multiplyYZ = Expression.Multiply(parameterY, parameterZ);
Expression addXMultiplyYZ = Expression.Add(parameterX, multiplyYZ);
Func<int,int,int,int> f = Expression.Lambda<Func<int, int, int, int>>
(
    addXMultiplyYZ,
    parameterX,
    parameterY,
    parameterZ
).Compile();
Console.WriteLine(f(24, 6, 3)); // prints 42 to the console

【讨论】:

  • +1 这是一个很好的例子,但它是为有问题的字符串量身定制的。此示例无法帮助 OP 解析随机字符串,推断字符串中标识符的类型,并根据找到的内容创建方法。不过,作为一个很好的例子,+1 给你。
  • 我认为解析对 OP 来说比表达式树更熟悉(至少关于解析的文献更大)。表达式树的目的仅仅是向他展示技术并展示他们的力量,而不是解决一般问题。
【解决方案2】:

使用 CodeDom 只是为了好玩(字符串中允许任何有效的 C# 代码,只要它存在于 mscorlib 中(根本不检查错误):

static class Program
{
    static string code = @"
        public static class __CompiledExpr__
        {{
            public static {0} Run({1})
            {{
                return {2};
            }}
        }}
        ";

    static MethodInfo ToMethod(string expr, Type[] argTypes, string[] argNames, Type resultType)
    {
        StringBuilder argString = new StringBuilder();
        for (int i = 0; i < argTypes.Length; i++)
        {
            if (i != 0) argString.Append(", ");
            argString.AppendFormat("{0} {1}", argTypes[i].FullName, argNames[i]);
        }
        string finalCode = string.Format(code, resultType != null ? resultType.FullName : "void",
            argString, expr);

        var parameters = new CompilerParameters();
        parameters.ReferencedAssemblies.Add("mscorlib.dll");
        parameters.ReferencedAssemblies.Add(Path.GetFileName(Assembly.GetExecutingAssembly().Location));
        parameters.GenerateInMemory = true;

        var c = new CSharpCodeProvider();
        CompilerResults results = c.CompileAssemblyFromSource(parameters, finalCode);
        var asm = results.CompiledAssembly;
        var compiledType = asm.GetType("__CompiledExpr__");
        return compiledType.GetMethod("Run");
    }

    static Action ToAction(this string expr)
    {
        var method = ToMethod(expr, new Type[0], new string[0], null);
        return () => method.Invoke(null, new object[0]);
    }

    static Func<TResult> ToFunc<TResult>(this string expr)
    {
        var method = ToMethod(expr, new Type[0], new string[0], typeof(TResult));
        return () => (TResult)method.Invoke(null, new object[0]);
    }

    static Func<T, TResult> ToFunc<T, TResult>(this string expr, string arg1Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T) }, new string[] { arg1Name }, typeof(TResult));
        return (T arg1) => (TResult)method.Invoke(null, new object[] { arg1 });
    }

    static Func<T1, T2, TResult> ToFunc<T1, T2, TResult>(this string expr, string arg1Name, string arg2Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T1), typeof(T2) },
            new string[] { arg1Name, arg2Name }, typeof(TResult));
        return (T1 arg1, T2 arg2) => (TResult)method.Invoke(null, new object[] { arg1, arg2 });
    }

    static Func<T1, T2, T3, TResult> ToFunc<T1, T2, T3, TResult>(this string expr, string arg1Name, string arg2Name, string arg3Name)
    {
        var method = ToMethod(expr, new Type[] { typeof(T1), typeof(T2), typeof(T3) },
            new string[] { arg1Name, arg2Name, arg3Name }, typeof(TResult));
        return (T1 arg1, T2 arg2, T3 arg3) => (TResult)method.Invoke(null, new object[] { arg1, arg2, arg3 });
    }

    static void Main(string[] args)
    {
        var f = "x + y * z".ToFunc<int, int, long, long>("x", "y", "z");
        var x = f(3, 6, 8);

    }
}

【讨论】:

    【解决方案3】:

    C# 没有这样的功能(其他语言——比如 JavaScript——有eval 函数来处理这样的事情)。您需要自己解析字符串并使用表达式树或发出 IL 创建一个方法。

    【讨论】:

      【解决方案4】:

      .Net 框架中有执行此操作的功能。

      这并不容易。您需要在语句周围添加一些代码,使其成为一个完整的程序集,包括您可以调用的类和方法。

      然后将字符串传递给

      CSharpCodeProvider.CompileAssemblyFromSource(options, yourcode);
      

      Here is an example

      【讨论】:

      • 以及弄清楚需要哪些参数等。很难与 CompileAssemblyFromSource 友好。
      【解决方案5】:

      可以使用语法(例如 ANTLR)和创建表达式树的解释器。这不是一项小任务,但是,如果您限制您接受的输入范围,您就可以成功。以下是一些参考资料:

      下面是一些将 ANTLR ITree 转换为表达式树的代码。它并不完整,但会向您展示您面临的挑战。

      private Dictionary<string, ParameterExpression> variables
          = new Dictionary<string, ParameterExpression>();
      
      public Expression Visit(ITree tree)
      {
          switch(tree.Type)
          {
              case MyParser.NUMBER_LITERAL:
                  {
                      float value;
                      var literal = tree.GetChild(0).Text;
                      if (!Single.TryParse(literal, out value))
                          throw new MyParserException("Invalid number literal");
                      return Expression.Constant(value);
                  }
      
              case MyParser.IDENTIFIER:
                  {
                      var ident = tree.GetChild(0).Text;
                      if (!this.variables.ContainsKey(ident))
                      {
                          this.variables.Add(ident,
                             Expression.Parameter(typeof(float), ident));
                      }
      
                      return this.variables[ident];
                  }
      
              case MyParser.ADD_EXPR:
                  return Expression.Add(Visit(tree.GetChild(0)), Visit(tree.GetChild(1)));
      
              // ... more here
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多