所以我正在做一个需要解析表达式树的项目。我的大部分工作正常,但遇到了一点问题。
我一直在查看关于表达式树的 StackOverflow 上的其他问题,但似乎找不到我的问题的答案,所以这里开始。
我的问题是常量和变量之间的差异(或缺失)。让我从一个例子开始:
user => user.Email == email
这显然不是常量而是变量,但它最终成为表达式树中某处的 ConstantExpression。如果您看一下表达式本身,它看起来有点奇怪:
Expression = {value(stORM.Web.Security.User+<>c__DisplayClassa)}
如果我们再举一个例子:
task => task.Status != TaskStatus.Done && t.Status != TaskStatus.Failed
这里我使用了一个 ENUM (TaskStatus)。
所以我的问题是,在树解析中,我似乎在两种情况下都以 ConstantExpression 结束,我真的需要能够区分它们。这些只是示例,所以我要问的是一种通用的方式来区分这两种类型的表达式,这样我就可以在解析中以两种不同的方式处理。
编辑:好吧,我的例子可能不够清楚,所以我再试一次。第一个例子:
用户user = db.Search < User > (u => u.Email == email);
我正在尝试查找具有给定电子邮件地址的用户。我正在将其解析为存储过程,但我想这不是重点。
第二个例子:
IList
在这里,我试图找到状态不同于“完成”和“失败”的所有任务。 这再次被解析为存储过程。在第一个示例中,我的代码需要确定存储过程需要一个输入参数,即电子邮件变量的值。在第二个示例中,我不需要任何输入参数,我只需要创建用于选择状态不同于“完成”和“失败”的任务的 SQL。
再次感谢
最佳答案
所以从表达式的角度来看,该值是一个常量。它不能被表达式改变。
您拥有的是一个潜在的开放式闭包 - 即值可以在表达式执行之间更改,但不会在执行期间更改。所以它是一个“常数”。这是函数式编程和非函数式 :) 编程之间的范式差异。
考虑
int a =2;
Expression<Func<int, int>> h = x=> x+ a;
Expression<Func<int, int>> j = x => x +2;
a = 1;
术语 a 是对匿名类的成员访问,匿名类包装并访问堆栈上的 a 变量。第一个节点是 MemberAccess 节点,然后在它下面 - 表达式是一个常量。
对于上面的代码:
((SimpleBinaryExpression)(h.Body)).Right
{value(WindowsFormsApplication6.Form1+<>c__DisplayClass0).a}
CanReduce: false
DebugView: ".Constant<WindowsFormsApplication6.Form1+<>c__DisplayClass0>(WindowsFormsApplication6.Form1+<>c__DisplayClass0).a"
Expression: {value(WindowsFormsApplication6.Form1+<>c__DisplayClass0)}
Member: {Int32 a}
NodeType: MemberAccess
Type: {Name = "Int32" FullName = "System.Int32"}
下面的常量:
((MemberExpression)((SimpleBinaryExpression)(h.Body)).Right).Expression
{value(WindowsFormsApplication6.Form1+<>c__DisplayClass0)}
CanReduce: false
DebugView: ".Constant<WindowsFormsApplication6.Form1+<>c__DisplayClass0>(WindowsFormsApplication6.Form1+<>c__DisplayClass0)"
NodeType: Constant
Type: {Name = "<>c__DisplayClass0" FullName = "WindowsFormsApplication6.Form1+<>c__DisplayClass0"}
Value: {WindowsFormsApplication6.Form1.}
}
}
普通的旧 2 结果是:
((SimpleBinaryExpression)(j.Body)).Right
{2}
CanReduce: false
DebugView: "2"
NodeType: Constant
Type: {Name = "Int32" FullName = "System.Int32"}
Value: 2
不知道对你有没有帮助。您可以通过查看父节点或父节点访问的对象类型来判断。
由于您的澄清而添加 -
所以当你说
user => user.Email == email
您的意思是查找所有电子邮件地址等于传入参数的用户 - 但是该链接表达式的含义完全不同。
你想说的是
Expression<Func<User, string, bool>> (user, email) => user.Email == email
这样,电子邮件现在将成为一个参数。如果您不喜欢这样,您还可以做另一件事。
第二个例子会工作得很好 - 不需要额外的参数 consts 将是 consts。
t => t.Status != TaskStatus.Done && t.Status != TaskStatus.Failed
编辑:添加另一种方式:
因此,为了使代码正常工作,您必须做的一件事是在 lambda 之外声明一个字符串电子邮件 - 这有点笨拙。
您可以通过将参数按常规放在特定位置(例如静态类)来更好地识别参数。然后,当通过 Lambda 时,您不必查看一些可怕的 cloture 对象 - 而是您制作的一个不错的静态类。
public static class Parameter
{
public static T Input<T>(string name)
{
return default(T);
}
}
然后你的代码看起来像这样:
Expression<Func<User, bool>> exp = x => x.Email == Parameter.Input<String>("email");
然后您可以遍历树 - 当您调用 Parameter 静态类时,您可以查看类型和名称(在参数集合中),然后继续......
关于c# - 表达式树解析,变量最终成为常量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4677295/