将字符串解析为C#lambda Func

时间:2009-11-10 13:09:46

标签: c# linq

有没有办法将lambda的字符串表示转换为lambda Func?

Func<Product, bool> func = Parse<Product, bool>("product => product.Name.Length > 0");

我尝试了动态LINQ,但它没有按预期工作 - 例如,它不期望lambda语法=&gt;。

答案摘要:

  • 编写我自己的C#编译器 - 非常有趣
  • 启动外部编译器(如csc.exe) - 非常慢
  • 使用DLINQ - 正如我所说,我看不出它如何解析 lambda 表达式

为什么我需要这个:因为没有办法将lambdas传递给自定义属性,比如

[Secure(role => role.CanDoThis && role.AllowedCount > 5)]

因此,作为一种解决方法,我想将lambda作为字符串传递:“role =&gt; role.CanDoThis&amp;&amp; role.AllowedCount&gt; 5”。但似乎我必须像这样使用DLINQ:“CanDoThis&amp;&amp; AllowedCount&gt; 5” - 因为这是它理解的语法。但我的问题是关于真正的lambdas,我在询问时已经使用过DLINQ了。

5 个答案:

答案 0 :(得分:7)

它们是许多可用的lambda表达式解析器。其中一些是Lambda-ParserSprache

示例代码:

示例1:字符串concat和数字计算:

string code = "2.ToString()+(4*2)"; // C# code Func<string> 
func = ExpressionParser.Compile<Func<string>>(code); // compile code 
string result = func(); // result = "28"

答案 1 :(得分:6)

您可以解析字符串并使用Expression类构建lambda表达式,基本上复制了编译器的功能。

答案 2 :(得分:5)

我猜你必须求助于CSharpCodeProvider。但是,处理所有可能的局部变量引用可能并不简单。您如何告诉CSharpCodeProvider有关lambda参数的类型?我可能会创建一个如下所示的模板类:

class ExpressionContainer {
    public Expression<Func<Product, bool>> TheExpression;
    public string Length;

    public ExpressionContainer() {
        TheExpression = <user expression text>;
    }
}

然后做这样的事情:

string source = <Code from above>;
Assembly a;
using (CSharpCodeProvider provider = new CSharpCodeProvider(...) {
    List<string> assemblies = new List<string>();
    foreach (Assembly x in AppDomain.CurrentDomain.GetAssemblies()) {
        try {
            assemblies.Add(x.Location);
        }
        catch (NotSupportedException) {
            // Dynamic assemblies will throw, and in .net 3.5 there seems to be no way of finding out whether the assembly is dynamic before trying.
        }
    }

    CompilerResults r = provider.CompileAssemblyFromSource(new CompilerParameters(assemblies.ToArray()) { GenerateExecutable = false, GenerateInMemory = true }, source);
    if (r.Errors.HasErrors)
        throw new Exception("Errors compiling expression: " + string.Join(Environment.NewLine, r.Errors.OfType<CompilerError>().Select(e => e.ErrorText).ToArray()));
    a = r.CompiledAssembly;
}
object o = a.CreateInstance("ExpressionContainer");
var result = ( Expression<Func<Product, bool>>)o.GetType().GetProperty("TheExpression").GetValue(o);

但是,请注意,对于长时间运行的应用程序,您应该在单独的应用程序域中创建所有这些内存中程序集,因为它们在卸载它们所在的应用程序域之前无法释放。

答案 3 :(得分:1)

您可以使用CSharpCodeProvider执行某些操作(使用更多代码包装表达式以创建有效类并将其编译为程序集,然后加载程序集)。

我相信LINQPad就是这样做的。

答案 4 :(得分:1)

回答你更具体的问题,(你可能已经知道了,但我会尝试提一下),你可以创建一个字典,将可以是常数(整数或枚举)的值映射到lambda。

sealed class Product {
   public bool CanDoThis { get; set; }
   public int AllowedCount { get; set; }
}

public enum SecureFuncType {
   Type1,
   Type2,
   Type3
}

sealed class SecureAttribute : Attribute {
   [NotNull] readonly Func<Product, bool> mFunc;

   public SecureAttribute(SecureFuncType pType) {
      var secureFuncs = new Dictionary<SecureFuncType, Func<Product, bool>> {
         { SecureFuncType.Type1, role => role.CanDoThis && role.AllowedCount > 1 },
         { SecureFuncType.Type2, role => role.CanDoThis && role.AllowedCount > 2 },
         { SecureFuncType.Type3, role => role.CanDoThis && role.AllowedCount > 3 }
      };

      mFunc = secureFuncs[pType];
   }
}

[Secure(SecureFuncType.Type1)]
sealed class TestClass {
}

// etc...