我需要构建一个系统,其中我有许多存储在文件中的表达式。这些表达式将被读入程序,编译成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()),但这些主要是审美偏好。此外,由于可以使用的此类方法的数量很少,因此可以在内部重写表达式,以便从类似函数调用的表达式转换为方法调用。
感谢大家指点我正确的方向。
答案 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
);
expr
和expr2
功能相同。