c# - 表达式树解析,变量最终成为常量

标签 c# .net lambda tree expression

所以我正在做一个需要解析表达式树的项目。我的大部分工作正常,但遇到了一点问题。

我一直在查看关于表达式树的 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 tasks = db.Search(t => t.Status != TaskStatus.Done && t.Status != TaskStatus.Failed);

在这里,我试图找到状态不同于“完成”和“失败”的所有任务。 这再次被解析为存储过程。在第一个示例中,我的代码需要确定存储过程需要一个输入参数,即电子邮件变量的值。在第二个示例中,我不需要任何输入参数,我只需要创建用于选择状态不同于“完成”和“失败”的任务的 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/

相关文章:

C# CSV 解析转义双引号

c# - 通过 ExecuteNonQuery 检查表中的记录是否存在于数据库中

c# - 复制到另一个文件夹后如何删除 Zip 文件

c# - 编译器未解析为预期的扩展方法

jakarta-ee - 表达式语言 lambda 类型推断

c++ - 传入一个函数以获取一个或多个返回值

c# - SQLDataReader 在 ExecuteReader 之后读取为空

c# - 通过 System.Data.Linq.DataContext 插入 SQL Server 时出错

.net - 将 Linq/Lambda 表达式转换为匿名方法

.net - System.Activator.CreateInstance(T) 是否存在足以阻止我们随意使用它的性能问题?