LINQ to Objects系列(4)表达式树

时间:2022-01-11 13:56:54

  为了进一步加深对Lambda表达式的理解,我们需要掌握一个新的知识,Lambda表达式树,可能听名字看起来很高深和难以理解,但实际上理解起来并没有想象中那么难,这篇文章我想分以下几点进行总结。

1,表达式树的语法

2,将代码转换到数据

3,探索表达式树

4,将数据转换到代码

5,IQueryable<T>和表达式树

6,为什么要将LINQ to SQL查询表达式转换成表达式树?

7,IQueryable<T>和IEnumerable<T>

8,总结

表达式树的语法

//利用Lambda表达式定义一个Func委托
Func<int, int, int> function = (a, b) => a + b;

变量function指向两个数字相加的原生可执行的代码,上面这个Lambda表达式等价于下面这个方法。

public int function(int a, int b)
{
return a + b;
}

这个Lambda表达式和这个方法都可以这样调用。

int c = function(,); //结果为:3

将代码转换到数据

表达式树不是一段可执行代码,而是一种数据结构。那么怎么将Lambda表达式转换成表达式树呢?

我们可以使用命名空间System.Linq.Expressions下的Expression类来实现这个需求。

例如我们先创建一个表达式树,如下代码。

//创建表达式树
Expression<Func<int, int, int>> expression = (a, b) => a + b;

这样,我们就创建一个类型为Expression<T>的表达式树,标识expression不是可执行代码;它是一个名叫表达式树的数据结构。我们可以使用工具ExpressionTreeVisualizer来浏览表达式树,如下图。

LINQ to Objects系列(4)表达式树

探索表达式树

Expression<TDelegate>类有四个属性:

  • Body: 得到表达式的主体。
  • Parameters: 得到lambda表达式的参数.
  • NodeType: 获取树的节点的ExpressionType。共45种不同值,包含所有表达式节点各种可能的类型,例如返回常量,例如返回参数,例如取两个值的小值(<),例如取两个值的大值(>),例如将值相加(+),等等。
  • Type: 获取表达式的一个静态类型。在这个例子里,表达式的类型是Func<intintint>。

那么怎么查看表达式树中的参数名称呢?从上图中我们可以看出,参数是一个ReadOnlyCollection集合,所以我们可以通过索引来访问。如下代码。

//访问表达式树的参数
Console.WriteLine("参数1:{0},参数2:{1}",expression.Parameters[],expression.Parameters[]);

接下来,怎么查看表达式树的Body体呢?在这个例子里是(a+b)。代码如下。

//访问表达式树的Body
BinaryExpression body = expression.Body as BinaryExpression;
ParameterExpression left = body.Left as ParameterExpression;
ParameterExpression right = body.Right as ParameterExpression;
Console.WriteLine(expression.Body);
Console.WriteLine(" 表达式左边部分: " + "{0}{4} 节点类型: {1}{4} 表达式右边部分: {2}{4} 类型: {3}{4}", left.Name, body.NodeType, right.Name, body.Type, Environment.NewLine);

输出结果为:

aaarticlea/png;base64,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" alt="" />

通过探索表达式树,我们可以分析表达式的各个部分发现它的组成。你可以看见,我们的表达式的所有元素都展示为像节点这样的数据结构。表达式树是代码转换成的数据。

将数据转换到代码

我们可以将代码转换为数据,那么我们也可将数据转换为代码。下面的代码说明了如果将数据(表达式树数据结构)转换为代码。

//将数据(表达式树)转换为代码
int result = expression.Compile()(,);
Console.WriteLine(result); //输出结果为:3

可以发现,程序输出结果与Lambda表达式执行结果一样。

IQueryable<T>和表达式树

现在至少你有一个抽象的概念理解表达式树,现在是时候回来理解其在LINQ中的关键作用了,尤其是在LINQ to SQL中。花点时间考虑这个标准的LINQ to SQL查询表达式:

var query = from c in db.Customers
where c.City == "Nantes"
select new { c.City, c.CompanyName };

你可能知道,这里LINQ表达式返回的变量query是IQueryable类型。这里是IQueryable类型的定义:

public interface IQueryable : IEnumerable
{
Type ElementType { get; }
Expression Expression { get; }
IQueryProvider Provider { get; }
}

你可以看见,IQueryable包含一个类型为Expression的属性,Expression是Expression<T>的基类。IQueryable的实例被设计成拥有一个相关的表达式树。它是一个等同于查询表达式中的可执行代码的数据结构。

为什么要将LINQ to SQL查询表达式转换成表达式树?

现在我们知道,表达式树是一个用来表示可执行代码的数据结构。那我们为什么要将LINQ to SQL查询表达式转换成表达式树呢?

一个LINQ to SQL查询不是在C#程序里执行的,而是被转换成SQL语句,通过网络发送,最后在数据库服务器上执行的。也就是说,下面这个LINQ查询不是在C#程序里执行的。

var query = from c in db.Customers

            where c.City == "Nantes"

            select new { c.City, c.CompanyName };

它是被转换成SQL语句后在数据库服务器上运行的。转换后的SQL语句如下代码。

SELECT [t0].[City], [t0].[CompanyName]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[City] = @p0

现在也许可以回答上面的问题。可以用一句话总结:表达式树是为了更方面地将查询表达式转换成字符串(这里指的是SQL语句)并交给其它程序(这里一般指数据库服务器)执行。

IQueryable<T>和IEnumerable<T>

我们知道,LINQ to Objects通常返回IEnumerable<T>,而LINQ to SQL返回的是IQueryable<T>。那为什么它们返回的类型会不一样呢?

我们先来看它们的定义,也许我们可以从它们的定义中找到问题的答案。

IEnumberable<T>的定义如下:

public interface IEnumerable<T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}

IQueryable<T>的定义如下:

public interface IQueryable : IEnumerable
{
Type ElementType { get; }
Expression Expression { get; } //表达式树
IQueryProvider Provider { get; }
}

可以看出,IQueryable<T>包含一个Expression表达式树的定义而IEnumberable<T>却没有,这同时也揭示了一个现象,表达式树通常用在LINQ to SQL查询中,而LINQ to Objects中却很少使用。

那为什么LINQ to Objects中很少使用表达式树呢?是因为LINQ to Objects查询通常在.net程序中就可以完成,不需要将其转换成字符串(或SQL语句)发送到其它程序中执行。

那么针对这两种返回类型,我们该怎么选择呢?这里有两条原则可以参考:

  • 如果查询表达式可以在本程序里执行的,那么使用Enumberable<T>就可以完成任务。
  • 如果查询表达式需要被转换成字符串并发送到其它程序中执行的,那么就应该使用IQueryable<T>和表达式树。

总结

通过以上内容的学习,发现表达式树并没有想象中那么难以理解,关于表达式树我想用以一几句通俗易懂的话总结。

  1. 表达式树是一种用来表示可执行代码(一般指Lambda查询表达式)的树形数据结构。
  2. 表达式树在LINQ to SQL中使用得非常多(因为LINQ to SQL查询返回IQueryable<T>类型),通过表达式树,LINQ to SQL查询表达式更方便地被解析成SQL语句并发送到数据库服务器上执行。
  3. 一条最佳实践原则:LINQ查询如果在程序内执行的不需要表达式树,当代码在程序外部执行时则需要使用表达式树。