使用方法

时间:2015-07-27 12:56:11

标签: c# linq expression-trees dynamic-linq

我需要构建一个系统,其中我有许多存储在文件中的表达式。这些表达式将被读入程序,编译成linq表达式并在许多对象上作为函数进行求值。但是,这些表达式将包含对代码中某些函数的引用(即它们不仅仅是基本的C#运算符)。

我尝试使用System.Linq.Dynamic中的DynamicExpression,它几乎可以正常工作,但我无法识别代码中的函数。基本上,当我在下面有这个代码时:

public class GeoObj
{
    public int layer;
    public Polygon poly;
}

class Program
{
    public static bool ComplicatedMethod(GeoObj x, GeoObj y)
    {
        bool result = // some quite complicated computation on x and y, say a polygon intersection test
        return result;
    }

    static void Main(string[] args)
    {
        GeoObj o1 = new GeoObj(); // here set o1 fields
        GeoObj o2 = new GeoObj(); // here set o2 fields

        string sExpr = @"(x.layer == y.layer) && (ComplicatedMethod(x,y))";

        var xparam = Expression.Parameter(typeof(GeoObj), "x");
        var yparam = Expression.Parameter(typeof(GeoObj), "y");

        Expression<Func<GeoObj, GeoObj, bool>> expr = (Expression<Func<GeoObj, GeoObj, bool>>)System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { xparam, yparam }, typeof(bool), sExpr);

        var del2 = expr.Compile();
        Console.WriteLine(del2(o1, o2));
    }
}

(当然,在最终版本中,sExpr将从文本文件中读取,但这只是一个例子。)

代码在ParseLambda抛出一个异常,说明&#34; ComplicatedFunction&#34;不知道。我可以看到ParseLambda如何不知道&#34; ComplicatedFunction&#34;因为它不知道它在哪个组件中使用过。

有没有办法将该字符串编译成实际表达式?如果我使用硬编码的代码在我的代码中创建表达式:

Expression<Func<GeoObj, GeoObj, bool>> expr = (x, y) => (x.layer == y.layer) && ComplicatedFunction(x,y));

它按照我的意图运作,但我无法预料到所有可能的表达方式。

如果不可能,我想下一个最好的事情是将字符串表达式解析为解析树,并从该树创建表达式。由于将只使用几个函数(例如&#34; ComplicatedFunction&#34;),它看起来很可行。从这样的表达式创建语法树的最佳方法/最佳代码是什么?当一个符号未知时(比如&#34; ComplicatedFunction&#34;),我需要一些不会抛出异常的东西,这样我就可以很容易地解析树来构建表达式。

谢谢。

-------------------- UPDATE -------------------------- -------

@ pil0t的回答让我走上正确的道路来解决这个问题。基本上,您必须下载Dynamic Linq的原始源文件并进行两次修改。

寻找:

static readonly Type[] predefinedTypes = {

并删除&#39; readonly&#39;关键字(因此可以修改)。

然后,将以下函数添加到ExpressionParser类:

    public static void AddPredefinedType(Type type)
    {
        predefinedTypes = predefinedTypes.Union(new[] { type }).ToArray();
        keywords = ExpressionParser.CreateKeywords();
    }

你可以使用这样的新功能:

using System.Linq.Dynamic;
...
ExpressionParser.AddPredefinedType(typeof(GeoObj));

关于表达式语法的一个注释。新添加的功能可让您访问班级的方法&#39; GeoObj&#39; (或者你使用的任何课程),只有那些。这意味着您必须按如下方式修改表达式:

class GeoObj {
    public bool field1;
    public bool method1() { /* return something */ }
    public static ComplicatedMethod(GeoObj o1, GeoObj o2) { ... }
}
...
string myExpr = @"x.field1 && y.method1() && GeoObj.ComplicatedMethod(x,y)";

换句话说,你需要使用的所有函数都需要在类中移动,就像@Shlomo暗示的那样。我会喜欢更清晰的符号(例如使用&#34; ComplicatedMethod(x,y)&#34;而不是&#34; GeoObj.ComplicatedMethod(x,y)&#34;或者有类似的东西Method1(x)代替x.Method1()),但这些主要是审美偏好。此外,由于可以使用的此类方法的数量很少,因此可以在内部重写表达式,以便从类似函数调用的表达式转换为方法调用。

感谢大家指点我正确的方向。

2 个答案:

答案 0 :(得分:1)

我通过使用

修补原始资源来完成此操作
public class Foo
{
...
        public static void AddPredefinedType(Type type)
        {
            ExpressionParser.predefinedTypes = ExpressionParser.predefinedTypes.Union(new[] { type }).ToArray();
            ExpressionParser.CreateKeywords();
        }
}

之后,你可以使用

Foo.AddPredefinedType(typeof(Program));

并且程序中的所有方法都可用

答案 1 :(得分:0)

我无法真正帮助你解决DynamicExpression的问题。如果您正在寻找如何构建表达式,这可能有助于您入门。

假设您的班级GeoObj如下所示:

class GeoObj
{
    public static bool ComplicatedFunction(GeoObj a, GeoObj b)
    {
        return false;
    }

    public object layer { get; set; }
}

以下是如何构建与示例中的表达式类似的表达式:

Expression<Func<GeoObj, GeoObj, bool>> expr = (x, y) => (x.layer == y.layer) && GeoObj.ComplicatedFunction(x,y);

var complicatedFunctionMethodInfo = typeof (GeoObj).GetMethod("ComplicatedFunction");
var paramX = Expression.Parameter(typeof (GeoObj), "x");
var paramY = Expression.Parameter(typeof (GeoObj), "y");
var expr2 = Expression.Lambda<Func<GeoObj, GeoObj, bool>>(
    Expression.AndAlso
    (
        Expression.Equal
        (
            Expression.Property(paramX, "layer"),
            Expression.Property(paramY, "layer")
        ),
        Expression.Call(complicatedFunctionMethodInfo, paramX, paramY)
    ),
    paramX,
    paramY
);

exprexpr2功能相同。