【问题标题】:Expression Trees .net表达式树.net
【发布时间】:2010-11-10 15:19:30
【问题描述】:

我想很好地掌握这个主题,因此想向社区提出以下问题:

  1. 表达式树有什么好处?
  2. 这句话到底是什么意思:“表达式树是编译为代码的数据”?
  3. .net 上下文中的表达式树和函数式编程解决了哪些常见挑战?
  4. 有什么好的在线资源可以加快主题速度?

【问题讨论】:

    标签: .net functional-programming expression expression-trees


    【解决方案1】:

    表达式树有什么好处?

    表达式树允许程序在运行时以简化的方式操作它自己的部分实现。它们允许将表达式作为其结构的表示来传递,而不仅仅是可以调用的委托。

    这句话到底是什么意思:“表达式树是编译为代码的数据”?

    C#/.NET 之前的语言已经支持这种操作……最好的例子是 LISP。在程序内的数据结构中表示程序结构的能力称为homoiconicity。 C# 以表达式树的形式支持有限的同音性。 C# 语言允许从代码中的(子集)表达式透明地创建表达式树。例如:

    int x = 3;
    Expression<Func<bool>> IsXLessThan4Expr = () => x < 4;
    
    Func<bool> IsXLessThan4 = () => x < 4;
    

    变量IsXLessThan4Expr 是从 lambda 表达式中捕获的表达式树。如果我们想了解它的结构是什么,我们现在可以遍历该表达式的表示 - 如果我们愿意,可以操纵它。相比之下,委托IsXLessThan4 不能被检查……它只能被调用。当然,您可以始终获取任何方法的原始 IL(假设您拥有必要的权限)——但是从 IL 反转程序的逻辑结构比从表达式树反转要困难得多。

    .net 环境中的表达式树和函数式编程解决了哪些常见挑战?

    使用表达式树解决重要问题的最佳示例是在 LINQ-to-SQL 中,其中IQueryable 的实现能够将用 C# 编写的查询表达式树转换为等效的 SQL 查询,可以由数据库执行。

    表达式树还可以动态生成 C# 代码 - 因为表达式树可以编译为 lambda。这是一个例子:

    var paramNotification = 
          Expression.Parameter(typeof (NotificationEntry), "noti");
    
    Func<NotificationEntry, bool> predicate = 
            m_PredicateExpr = Expression.Lambda<Func<NotificationEntry, bool>>(
                Expression.LessThan(
                    Expression.Property(paramNotification, "Value"),
                    Expression.Constant(100)),
                new[] {paramNotification})
            .Compile();
    

    上面的 sn-p 创建了一个表达式,将 NotificationEntry 对象的 Value 字段与一些提供的常量 (100) 进行比较 - 并将其编译成我们可以调用的 lambda。

    有什么好的在线资源可以加快这个主题的速度?

    MSDN 可能是您目前最好的选择。

    【讨论】:

    • 好的,那么可以将表达式树称为动态代码片段,它表示一个逻辑表达式来操作一些可在运行时应用于结构的数据?
    • @Max:不完全是。表达式树本身不是代码。它是一种数据结构。您可以使用 Compile() 将表达式树转换为代码 - 这将返回适当的委托。
    • 我认为你的回答帮助我理解了主题。谢谢!我之前不知道的是,委托或 lambda 与表达式之间的主要区别在于,表达式可以在代码决定调用它之前进行分析和操作。因此,在许多 Linq2Whatever Provider 实现中都实现了访问者类,对吗?
    • @Max:是的,没错。有许多 IQueryable 实现使用检查和转换表达式树的能力来允许 C# 代码表示对其他类型资源的查询。他们通过访问表达式的表示并将其转换为适合其领域的形式来做到这一点。真正好的是,在 C# 中,一个 lambda 可以透明地传递给任何接受 Expression&lt;...&gt; 类型参数的方法 - 这允许以自然的方式编写消费代码,而不必担心转换的底层细节。
    • 太好了,这很有意义,所以我想我应该编写自己的 Linq Provider 以便更好地理解机制。
    【解决方案2】:
    1. 它们带来了获取源代码并在执行时“理解”它的能力 - LINQ 提供程序是主要目标

    2. 嗯,表达式树通常是从源代码构建的(例如,转换后的 lambda 表达式),但不是将源代码编译成 可执行文件 形式,而是构建成其他代码可以执行的树检查。 (更准确地说,编译器获取源代码,并发出可执行代码,然后在执行时构建表达式树。)

    3. 说实话,表达式树和函数式编程对我来说似乎有些正交。两者都经常处理高阶函数,但仅此而已。

    4. MSDN perhaps?

    【讨论】:

    • Linq 是否对所有实现都使用表达式树? LINQ到SQL? Linq2对象? Linq2Xml?这是否也意味着如果您要构建自己的 Linq 提供程序(实现 IQuaryable 和 IQueryProvider)将需要实现表达式树,如果是,为什么以及如何(当然是高级别的)?
    • @Max:不,只有一些实现使用表达式树。例如,对于 LINQ to Objects,lambda 表达式只是编译为委托。 (LINQ to XML 并不是真正的 LINQ 提供程序 - 它是一个 XML API,可以很好地与 LINQ to Objects 配合使用。)
    • 如果您构建自己的 LINQ 提供程序来实现 IQueryable,您将看到表示查询的表达式树 - 然后您需要检查这些表达式树并将它们转换为适当的格式。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多