一开始查询表达式总是针对某个序列

时间:2022-05-04 03:14:38

盘问表达式和LINQ to object(上)

本章内容:

流式措置惩罚惩罚数据和延迟执行序列

标准盘问操纵符和盘问表达式转换

范畴变量和透明标识符

投影、过滤和排序

联接和分组

选择要使用的语法

LINQ中的观点介绍

序列

你固然应该对序列这个观点觉得很熟悉: 它通过IEnumerable 和 IEnumerable< T> 接口进行封装,序列就像数据项的传送带——你每次只能获取它们一个, 直到你不再想获取数据, 或者序列中没有数据了。

序列与其他的数据调集布局对比最大的区别在于,你凡是不知道序列有几多项组成--或者不能访谒任意项,只能是当前这个。列表和数组也能作为序列, 因为 List< T> 实现了IEnumerable< T>—— 不过, 反过来并不总是可行。 好比,你不能拥有一个无限的数组或列表。

序列是LINQ的根本。一开始盘问表达式总是针对某个序列,跟着操纵的进行,可能会转换序列,也可能和更多的序列链接在一起。

来看一个列子:

var adaultNames = from person in People where person.Age > 18 select person.Name;

下面以图的形式将这个操纵拆分成了法式:

一开始查询表达式总是针对某个序列

在分化这个法式之前,先讲为什么序列在LINQ中的职位地方是如此重要: 这是由于, 它们是数据措置惩罚惩罚的流模型的根本, 让我们能够只在需要的时候才对数据进行获取和措置惩罚惩罚。

上图中每一个箭头代表一个序列——描述在左边, 示例数据在右边。 每个框都代表盘问表达式的一个法式。 最初,我们具有整个家庭成员(用Person东西暗示)。接着颠末过滤后, 序列就只包罗成人了(还是用Person东西暗示)。 而最终的功效以字符串形式包罗这些 成人的名字。每个法式就是得到一个序列, 在序列上应用操纵以生成新的序列。 功效不是字符串"Holly" 和"Jon"—— 而是 IEnumerable<String>, 这样,在从里面一个接一个获取元素的时候, 将首先生成"Holly", 其次得到"Jon"。

再来看一下背后的对象:首先创建的这个表达式,创建的这个表达式只是在内存中生成一个盘问的表示形式,这个表示形式使用委托来暗示的。只有在访谒这个功效集(adaultNames)的第一个元素的时候,整个”车轮“才会滚滚向前(把迭代器比方为车轮)。LINQ的这个 特点称为延迟执行。 在最终功效的第一个元素被访谒的时候, Select转换才会为它的第一个元素挪用Where转换。 而Where转换会访谒列表中的第一个元素, 查抄这个谓词是否匹配(在这个例子中,是匹配的), 并把这个元素返回给Select。 最后,依次提取着名称作 为功效返回。固然,相关的各类参数必需执行可控性查抄,如果你要实现本身的LINQ操纵符,服膺这一点非常重要。

下图展示了当用foreach挪用功效序列中的每一项时,盘问表达式在运行中的几个阶段

一开始查询表达式总是针对某个序列

这就是流式的特点,虽然涉及了几个阶段,不过,这种使用流的方法措置惩罚惩罚数据是很高效和灵活的。出格是,不管有几多数据源,在某个时间点上你只需要知道此中一个就好了。

与流式传输对比,还有一种缓冲式的,因为你有的时候必需吧序列中的元素全部加载到内存中来进行计算,好比Reverse操纵。他需要提取序列中的所有可用数据,以便把最后一个元素作为第一个元素返回。固然这在效率上会对整个表达式的执行造成很大的性能影响。

不管时流式传输还是缓冲式的传输,他们都属于延迟操纵,就是只有在枚举功效集中的第一个元素时才会真正的传输数据,与此相对的是当即执行——有一些转换一经挪用就会当即执行。一般来说,返回另一个序列的操纵(凡是是IEnumerable<T>和IQueryable<T>)会进行延迟操纵,返回单一值的运算会当即执行。


LINQ标准盘问操纵符

LINQ的标准盘问操纵符是一个转换的调集, 具有明确的含义。标准盘问操纵符拥有配合的含义,但在差此外LINQ供给器下,语义也差别,有的LINQ供给器可能在获取第一个元素的时候就加载了所有的数据,好比web处事。这提示我们在编写盘问操纵时要考虑到使用的是什么数据源。

C#3撑持的某些标准盘问操纵符通过盘问表达式内置到语言中。

linq to object的扩展包morelinq可以通过nuget下载。还有Reactive Extensions。

本章的实例数据