Linq系列:基础与本质(Part I)

之前写过一些C#3.x新的特性。请参考:C#3.x特性,我们知道这些新的特性基本都是为实现LINQ服务的,在平常的编程中也可以有选择的合理应用,也会有效提高编码效率,实现可读性比较强的简洁代码。在认识这些特性的基础上,理解认识LINQ将变得简单了。

1 LINQ简介:

LINQ 查询表达式(query expressions )可以使用统一的方式对实现IEnumerable<T>接口的对象、关系数据库、数据集(Datasets)以及XML文档进行访问。
严格的说,LINQ是用来描述一系列数据访问技术的术语。LINQ to Objects 适用于实现IEnumerable<T>接口的对象,LINQ to SQL 适用于关系数据库, LINQ to DataSet is 则是 LINQ to SQL的一个子集, LINQ to XML 适用于XML 文档。LINQ 查询表达式 是强类型的(strongly typed),因此编译器会确保其语法正确性。LINQ是一个可扩展技术,第三方可以使用扩展函数来定义新的查询操作符。

LINQ核心程序集(Assembly)

至少需要引用System.Linq 命名空间,在System.Core.dll中定义,Visual stuodio 2008默认会自动添加引用。

System.Core.dll

Defines the types that represent the core LINQ API. This is the one assembly you must have access to.

System.Data.Linq.dll

Provides functionality for using LINQ with relational databases (LINQ to SQL).

System.Data.DataSetExtensions.dll

Defines a handful of types to integrate ADO.NET types into the LINQ programming paradigm (LINQ to DataSet).

System.Xml.Linq.dll

Provides functionality for using LINQ with XML document data (LINQ to XML).

LINQ例子

Code

 

 我们看上面的代码定义了两个集合subset和subset1。分别通过查询表达式和Lambda表达式生成。那么LINQ内部到底是怎么实现的?这两种方式到底有什么不同呢。我们先来看看这段代码长生的IL。

 

Code

 

我们看看IL代码的1-11行是subset的长生过程,14-33行是subset1的长生过程。我们先不看25-33行的subset1代码,先关注1-11行,14-24行的代码。我们发现这里的代码除了使用不同的委托外,实际是一样的。先不对其讲解,我们就能确定,通过查询表达式和Lambda表达式生成两个集合subset和subset1对于CLR来说是一样的,没有什么区别。正如我之前在C#3.X系列提到的这些特性是基于编译器的新特性,在CLR层并没有提供新的实质内容,这里LINQ也是一样。编译器会最终实现一个语法映射的过程,将查询表达式翻译,映射成Lambda表达式的形式。

我们了解了其大观上的实现原理,那么我们就仔细看看其具体实现过程。请看IL代码的111行。这里是对where语法的实现,我们很容易的看到这里用到的一个委托。这个委托是编译器自动生成的一个静态委托量CS$<>9__CachedAnonymousMethodDelegate3。而这个变量正是来自于System.Func这个新类。这里我们就可以知道LINQ实质还是需要调用委托。除了委托,我们还可以看到编译器会生成一个静态方法:<QueryOverInts>b__0。在这个方法里对你的查询表达式的查询条件进行处理。LINQ实现的关键就是代码1011行。这里我们看到系统会调用System.Linq.Enumerable.Where<T>方法,结果集也正是通过此方法得到的,传入的第二个参数是由编译器生成的匿名方法,也就是上面说的委托变量。看到这里大家应该对LINQ的工作本质有个大概的了解。至于代码的2533行,是用System.Linq.Enumerable::OrderBy<int32,int32>方法去实现查询的Orderby语法。实现原理同以上对where的讲解。

我们对以上subset和subset1调用一下代码:

Code

 

 我们可以得出subset的类型是<WhereIterator>d__0`1,subset1的类型是OrderedEnumerable`2,可见LINQ表达式的形式不同,其结果类型就不同。但是由于以上Where和OrderBy都实现了IEnumerable<T>,所以可以写成上面的代码形式。根据以上分析,在获取LINQ的返回结果时,最好使用 var 关键字。如:

var subset = from i in numbers where i < 10 select i;

4 LINQ特性

我们对上面的实例代码加上下面几行:

Code

 

 我们可以看到subset结果集前后输出的不同点是一个为10,一个为5,其余元素一样。这里我们就可以看到LINQ 查询表达式只有在迭代访问其内容时,才会被计算并执行。这样可以保证每次访问得到的是最新的数据。

以上是查询的延时执行,那么查询的立即执行怎么实现呢?和简单,我们只需要在查询表达试ToList<T>()就可以了。将查询结果直接放到强类型的结果集中,执行后,这些结果集就和查询表达式没有关系了,可以单独操作。如:IEnumerable<int> subset3 = ( from i in numbers where i < 10 select i).ToList<int>();


这里我们经常会提到System.Linq.Enumerable System.Func。接下来将会分析这些。


待续。。。 

posted on 2008-11-12 14:54 gjcn 阅读(...) 评论(...) 编辑 收藏

导航

公告

统计