65.9K
CodeProject 正在变化。 阅读更多。
Home

理解表达式树

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.34/5 (24投票s)

2009年7月22日

Ms-PL

3分钟阅读

viewsIcon

62437

表达式树教程。

引言

C# 3.0 引入了许多有趣的功能,包括 Lambda 表达式、字段和集合初始化器、匿名类型、类型推断等等。其中之一是表达式树——一个与 LINQ 紧密相关的神秘主题。

本文将详细描述什么是表达式树。它还将向您展示如何在 C# 中构建和使用它们。文章最后将回答几个关于表达式树的重要问题——它们有什么特别之处?为什么以及何时使用它们?表达式树和 Lambda 表达式之间有什么区别?

本文假设您至少对 Lambda 表达式以及 LINQ(尤其是 LINQ to Objects)有基本的了解。

表达式树定义

一般来说,表达式树是特殊类型的二叉树。二叉树是一种树,其中所有节点包含零个、一个或两个子节点。为什么表达式树被实现为二叉树?主要是因为二叉树允许您快速找到您要查找的内容。在二叉树中找到所需信息的步骤的上限等于 log2N,其中 N 表示树中所有节点的数量。

让我们仔细考虑以下示例

Func<int, int, bool> f = (a, b) => a < b; 

这是一个 Lambda 表达式的简单声明,它接受两个数字作为参数,并在第一个数字小于第二个数字时返回 true,否则返回 false。很简单。但是,我们也可以用表达式树编写相同的逻辑。有什么区别?在回答这个问题之前,让我们看看如何创建它们。

构建表达式树

要创建引用前一个示例的表达式树,我们必须使用以下语法

Expression<Func<int, int, bool>> f = (a, b) => a < b; 

几乎一样。唯一的区别是使用了 Expression<T> 类。这个类有点方便,因为它包含四个有趣的属性,这些属性保存了关于底层表达式树的有用信息

  1. 正文
  2. NodeType
  3. 参数
  4. 类型

或者,您也可以用另一种方式构建相同的表达式树——一步一步地

ParameterExpression par1 = Expression.Parameter(typeof(int), "p1"); 
ParameterExpression par2 = Expression.Parameter(typeof(int), "p2"); 
BinaryExpression expr = Expression.LessThan(par1, par2); 
var f = Expression.Lambda<Func<int, int, bool>>(expr, 
                   new ParameterExpression[] { par1, par2 });

使用表达式树

到目前为止,我已经表明构建表达式树比构建简单的 Lambda 表达式更困难、更复杂。它们的使用方式也是如此。要使用表达式树,您必须首先编译它

var func = f.Compile();

从现在开始,您可以像使用任何其他函数一样使用它

var result = func(2,5); 

简直是多余的,不是吗?那么,如果使用 Lambda 表达式可以更简单、更快速地实现相同的目标,为什么要为表达式树烦恼呢?

表达式树的目的

在前面的示例中,我已经表明给定的表达式树在使用之前必须编译。这是因为表达式树实际上是一个数据结构,而不是编译后的代码。为什么?因为这段代码预计将在网络中使用,或者换句话说,在其他进程中使用(可能在其他计算机上运行)。例如,在我的机器上构建的 LINQ to SQL 查询将被发送到数据库服务器。服务器将自己构造一个特定的表达式,运行它,并返回请求的值。对于外部数据提供者来说,基于表达式树(即数据结构)构建一个适当的查询比基于编译后的代码更容易。

但是,这不适用于 LINQ to Objects。在这种情况下,您的查询将在调用它们的同一个进程中运行,并且无需创建任何特殊的数据结构来表示您的查询。这就是为什么在 LINQ to Objects 中可以使用 Lambda 表达式的原因。在所有其他情况下,您必须依赖表达式树。

摘要

表达式树是一个非常有趣的概念。一开始,它可能看起来只是复制了 Lambda 表达式提供的功能。然而,当使用与 LINQ to Objects 不同的 LINQ 类型时,这种差异是显而易见的。在这种情况下,您的查询将被发送到网络并在不同的进程中运行,甚至可能在不同的机器上运行。这是一个很酷的想法,使得这种场景成为可能,并且非常简单直接。

© . All rights reserved.