[C#] 进阶 - LINQ 标准查询操作概述

 

序 

  “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法。大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了IEnumerable<T> 接口或 IQueryable<T> 接口。标准查询运算符提供了包括筛选、投影、聚合、排序等功能在内的查询功能。  
     各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。返回单一值的方法(例如 Average 和 Sum)会立即执行。返回序列的方法会延迟查询执行,并返回一个可枚举的对象。  
     对于在内存中集合上运行的方法(即扩展 IEnumerable<T> 的方法),返回的可枚举对象将捕获传递到方法的参数。在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。  
     与之相反,扩展 IQueryable<T> 的方法不会实现任何查询行为,但会生成一个表示要执行的查询的表达式树。查询处理由源 IQueryable<T> 对象处理。
 

一、按标准执行方式分类

     标准查询运算符方法的 LINQ to Objects 实现采用两种主要方式之一来执行:立即执行延迟执行。采用延迟执行的查询运算符可以进一步分为两类:流式非流式
     
     1.执行方式
          (1)立即: 立即执行意味着在代码中声明查询的位置读取数据源并执行运算。  返回单个不可枚举的结果的所有标准查询运算符都立即执行
          (2)延迟: 延迟执行意味着不在代码中声明查询的位置执行运算。  仅当对查询变量进行枚举操作时才执行运算,例如通过使用 foreach 语句。这意味着查询的执行结果取决于执行查询而非定义查询时的数据源内容。如果多次枚举查询变量,则每次结果可能都不同。几乎所有返回类型为 IEnumerable<T> 或 IOrderedEnumerable<TElement> 的标准查询运算符都以延迟方式执行。 
           采用延迟执行方式的查询运算符可以另外分类为流式非流式
          ①流式运算符不需要在生成元素前读取所有源数据。在执行时,流式运算符一边读取每个源元素,一边对该源元素执行运算,并在可行时生成元素。流式运算符将持续读取源元素直到可以生成结果元素。这意味着可能要读取多个源元素才能生成一个结果元素。
          ②非流式运算符必须读取所有源数据才能生成结果元素。诸如排序分组等运算属于此类别。在执行时,非流式查询运算符读取所有源数据,将其放入数据结构中,执行运算,然后生成结果元素。
 

二、排列数据

     排序操作按一个或多个特性对序列的元素进行排序。第一个排序条件对元素执行主要排序。通过指定第二个排序条件,可以对各个主要排序组中的元素进行排序。 
     下图演示对一个字符序列执行按字母排序操作的结果。
  [C#] 进阶 - LINQ 标准查询操作概述

 

标准查询运算符操作方法 - 排序
方法名 说明 C# 查询表达式语法
OrderBy 按升序对值进行排序。 orderby
OrderByDescending 按降序对值进行排序。 orderby … descending
ThenBy 按升序执行次要排序。 orderby …, …
ThenByDescending 按降序执行次要排序。 orderby …, … descending
Reverse 颠倒集合中的元素的顺序。 X

  

  示例:

1 var words = new[] { "the", "quick", "brown", "fox", "jumps" };
2 var query = from word in words
3                    orderby word.Length
4                    select word;
5 
6 foreach (var word in query)
7 {
8     Console.WriteLine(word);
9 }
下面通过演示使用 orderby 进行升序排序:按字符串长度

 [C#] 进阶 - LINQ 标准查询操作概述

 

1 var words = new[] { "the", "quick", "brown", "fox", "jumps" };
2 var query = from word in words
3                    orderby word.Substring(0,1) descending 
4                    select word;
5 
6 foreach (var word in query)
7 {
8     Console.WriteLine(word);
9 }
下面通过演示使用 orderby descending 进行降序排序:按字符串的第一个字母

[C#] 进阶 - LINQ 标准查询操作概述

 

1 var words = new[] { "the", "quick", "brown", "fox", "jumps" };
2 var query = from word in words
3                    orderby word.Length, word.Substring(0, 1)
4                    select word;
5 
6 foreach (var word in query)
7 {
8     Console.WriteLine(word);
9 }
下面通过演示使用 orderby 进行主要和次要排序:先升序按字符串长度(主)、再升序按字符串的第一个字母(次)

[C#] 进阶 - LINQ 标准查询操作概述

 

1 var words = new[] { "the", "quick", "brown", "fox", "jumps" };
2 var query = from word in words
3                    orderby word.Length, word.Substring(0, 1) descending 
4                    select word;
5 
6 foreach (var word in query)
7 {
8     Console.WriteLine(word);
9 }
下面通过演示使用 orderby descending 进行主要和次要排序:先升序按字符串长度(主)、再降序按字符串的第一个字母(次)

 [C#] 进阶 - LINQ 标准查询操作概述

 

三、Set 操作

  LINQ 中的 Set 操作是指根据相同或不同集合中是否存在等效元素来生成结果集的查询操作

 

标准查询运算符操作方法 - Set
方法名 说明

C# 查询表达式语法

Distinct

从集合移除重复值。

X

Except

返回差集,差集是指位于一个集合但不位于另一个集合的元素。

X

Intersect

返回交集,交集是指同时出现在两个集合中的元素。

X

Union

返回并集,并集是指位于两个集合中任一集合的唯一的元素。

X

 

  图解 Set 操作

  (1)Distinct: 返回的序列包含输入序列的唯一元素。

[C#] 进阶 - LINQ 标准查询操作概述

  (2)Except: 返回的序列只包含位于第一个输入序列但不位于第二个输入序列的元素。  

[C#] 进阶 - LINQ 标准查询操作概述

  (3)Intersect: 返回的序列包含两个输入序列共有的元素。  

[C#] 进阶 - LINQ 标准查询操作概述

  (4)Union: 返回的序列包含两个输入序列的唯一的元素。 
[C#] 进阶 - LINQ 标准查询操作概述

[C#] 进阶 - LINQ 标准查询操作概述

 

 四、过滤数据

     筛选指将结果集限制为只包含某些满足指定条件的元素的操作。它又称为选择
     下图演示了对字符序列进行筛选的结果。筛选操作的谓词指定字符必须为“A”。
[C#] 进阶 - LINQ 标准查询操作概述

 

标准查询运算符操作方法 - 筛选
方法名 说明 C# 查询表达式语法
OfType 根据值强制转换为指定类型的能力选择值。 X
Where 选择基于谓词函数的值。 where
  
  示例:
 1 string[] words = { "the", "quick", "brown", "fox", "jumps" };
 2 
 3 var query = from word in words
 4                    where word.Length == 3
 5                    select word;
 6 
 7 foreach (var word in query)
 8 {
 9      Console.WriteLine(word);
10 }
使用 where 子句来从数组中筛选那些具有特定长度的字符串

[C#] 进阶 - LINQ 标准查询操作概述

 

五、量词操作 

  限定符运算返回一个 Boolean 值,该值指示序列中是否有一些元素满足条件或是否所有元素都满足条件。

     下图描述了两个不同源序列上的两个不同限定符运算。第一个运算询问是否有一个或多个元素为字符“A”,结果为 true。第二个运算询问是否所有元素都为字符“A”,结果为 true。  
[C#] 进阶 - LINQ 标准查询操作概述
 

标准查询运算符操作方法 - 量词

方法名

说明

C# 查询表达式语法

All

确定是否序列中的所有元素都满足条件。

X
Any

确定序列中是否有元素满足条件。

X

Contains

确定序列是否包含指定的元素。

X

 

六、投影操作

      投影是指将对象转换为一种新形式的操作,该形式通常只包含那些将随后使用的属性。通过使用投影,您可以构建依据每个对象生成的新类型。您可以映射属性,并对该属性执行数学函数。还可以在不更改原始对象的情况下映射该对象。
 
标准查询运算符操作方法 - 投影
方法名 说明 C# 查询表达式语法
Select 映射基于转换函数的值。 select
SelectMany 映射基于转换函数的值序列,然后将它们展平为一个序列。 from 子句
  
  示例
1 var words = new[] { "the", "quick", "brown", "fox", "jumps" };
2 var query = from word in words
3                    select word.Substring(0,1);
4 
5 foreach (var word in query)
6 {
7     Console.WriteLine(word);
8 }
Select:下面的示例使用 select 子句来映射字符串列表中每个字符串的第一个字母

[C#] 进阶 - LINQ 标准查询操作概述

 

 1 var phrases = new List<string>() { "an apple a day", "the quick brown fox" };
 2 
 3 var query = from phrase in phrases
 4                    from word in phrase.Split(' ')
 5                    select word;
 6 
 7 foreach (var word in query)
 8 {
 9    Console.WriteLine(word);
10 }
SelectMany:下面的示例使用多个 from 子句来映射字符串列表中每个字符串中的每个单词

[C#] 进阶 - LINQ 标准查询操作概述

  

  Select() 和 SelectMany() 的工作都是依据源值生成一个或多个结果值。Select() 为每个源值生成一个结果值。因此,总体结果是一个与源集合具有相同元素数目的集合。与之相反,SelectMany() 将生成单一总体结果,其中包含来自每个源值的串联子集合。作为参数传递到 SelectMany() 的转换函数必须为每个源值返回一个可枚举值序列。然后,SelectMany() 将串联这些可枚举序列以创建一个大的序列。

  下面两个插图演示了这两个方法的操作之间的概念性区别。在每种情况下,假定选择器(转换)函数从每个源值中选择一个由花卉数据组成的数组。

  下图描述 Select() 如何返回一个与源集合具有相同元素数目的集合。

[C#] 进阶 - LINQ 标准查询操作概述

 

  下图描述 SelectMany() 如何将中间数组序列串联为一个最终结果值,其中包含每个中间数组中的每个值。

[C#] 进阶 - LINQ 标准查询操作概述

 

  示例

  下面的示例比较 Select() 和 SelectMany() 的行为。代码将通过从源集合的每个花卉名称列表中提取前两项来创建一个“花束”。在此示例中,转换函数 Select 使用的“单一值”本身就是一个值集合。这需要额外的 foreach 循环,以便枚举每个子序列中的每个字符串。

 1         static void Main(string[] args)
 2         {
 3             var bouquets = new List<Bouquet>()
 4             {
 5                 new Bouquet {Flowers = new List<string> {"sunflower", "daisy", "daffodil", "larkspur"}},
 6                 new Bouquet {Flowers = new List<string> {"tulip", "rose", "orchid"}},
 7                 new Bouquet {Flowers = new List<string> {"gladiolis", "lily", "snapdragon", "aster", "protea"}},
 8                 new Bouquet {Flowers = new List<string> {"larkspur", "lilac", "iris", "dahlia"}}
 9             };
10 
11             IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);
12             IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);
13 
14             Console.WriteLine("query1 - Select():");
15             foreach (IEnumerable<string> collection in query1)
16             {
17                 foreach (var item in collection)
18                 {
19                     Console.WriteLine(item);
20                 }
21             }
22 
23             Console.WriteLine("\nquery2 - SelectMany():");
24             foreach (var item in query2)
25             {
26                 Console.WriteLine(item);
27             }
28 
29             Console.Read();
30         }
31 
32         class Bouquet
33         {
34             public List<string> Flowers { get; set; }
35         }        
View Code

相关文章: