LINQ to Objects系列(4)表达式树_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > LINQ to Objects系列(4)表达式树

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

 2014/5/18 13:21:15  永远的麦子  博客园  我要评论(0)
  • 摘要:为了进一步加深对Lambda表达式的理解,我们需要掌握一个新的知识,Lambda表达式树,可能听名字看起来很高深和难以理解,但实际上理解起来并没有想象中那么难,这篇文章我想分以下几点进行总结。1,表达式树的语法2,将代码转换到数据3,探索表达式树4,将数据转换到代码5,IQueryable<T>和表达式树6,为什么要将LINQtoSQL查询表达式转换成表达式树?7,IQueryable<T>和IEnumerable<T>8
  • 标签:表达式

  为了进一步加深对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(1,2); //结果为:3

将代码转换到数据

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

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

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

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

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

ExpressionTree

探索表达式树

Expression<TDelegate>类有四个属性:

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

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

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

接下来,怎么查看表达式树的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);

输出结果为:

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" alt="" />

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

将数据转换到代码

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

//将数据(表达式树)转换为代码
int result = expression.Compile()(1,2);
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查询如果在程序内执行的不需要表达式树,当代码在程序外部执行时则需要使用表达式树。
发表评论
用户名: 匿名