本篇主要介绍标准查询运算符的常用运算功能。

01 对数据排序

排序操作基于一个或多个属性对序列的元素进行排序。 第一个排序条件对元素执行主要排序。 通过指定第二个排序条件,您可以对每个主要排序组内的元素进行排序。

下图展示了对一系列字符执行按字母顺序排序操作的结果。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下节列出了对数据进行排序的标准查询运算符方法。

方法

OrderBy按升序对值排序。orderbyEnumerable.OrderBy

Queryable.OrderBy

OrderByDescending按降序对值排序。orderby … descendingEnumerable.OrderByDescending

Queryable.OrderByDescending

ThenBy按升序执行次要排序。orderby …, …Enumerable.ThenBy

Queryable.ThenBy

ThenByDescending按降序执行次要排序。orderby …, … descendingEnumerable.ThenByDescending

Queryable.ThenByDescending

Reverse反转集合中元素的顺序。不适用。Enumerable.Reverse

Queryable.Reverse

查询表达式语法示例

主要排序示例

主要升序排序

下面的示例演示如何在 LINQ 查询中使用 orderby 子句按字符串长度对数组中的字符串进行升序排序。

string[] words = { "the", "quick", "brown", "fox", "jumps" };

IEnumerable<string> query = from word in words
orderby word.Length
select word; foreach (string str in query)
Console.WriteLine(str); /* 输出:
the
fox
quick
brown
jumps
*/

主要降序排序

下面的示例演示如何在 LINQ 查询中使用 orderby descending 子句按字符串的第一个字母对字符串进行降序排序。

string[] words = { "the", "quick", "brown", "fox", "jumps" };

IEnumerable<string> query = from word in words
orderby word.Substring(, ) descending
select word; foreach (string str in query)
Console.WriteLine(str); /* 输出:
the
quick
jumps
fox
brown
*/

次要排序示例

次要升序排序

下面的示例演示如何在 LINQ 查询中使用 orderby 子句对数组中的字符串执行主要和次要排序。 首先按字符串长度,其次按字符串的第一个字母,对字符串进行升序排序。

string[] words = { "the", "quick", "brown", "fox", "jumps" };

IEnumerable<string> query = from word in words
orderby word.Length, word.Substring(, )
select word; foreach (string str in query)
Console.WriteLine(str); /* 输出:
fox
the
brown
jumps
quick
*/

次要降序排序

下面的示例演示如何在 LINQ 查询中使用 orderby descending 子句按升序执行主要排序,按降序执行次要排序。 首先按字符串长度,其次按字符串的第一个字母,对字符串进行排序。

string[] words = { "the", "quick", "brown", "fox", "jumps" };

IEnumerable<string> query = from word in words
orderby word.Length, word.Substring(, ) descending
select word; foreach (string str in query)
Console.WriteLine(str); /* 输出:
the
fox
quick
jumps
brown
*/
02 Set(集)运算

LINQ 中的集运算是指根据相同或不同集合(或集)中是否存在等效元素来生成结果集的查询运算。

下节列出了执行集运算的标准查询运算符方法。

方法

Distinct删除集合中的重复值。不适用。Enumerable.Distinct

Queryable.Distinct

Except返回差集,差集指位于一个集合但不位于另一个集合的元素。不适用。Enumerable.Except

Queryable.Except

相交返回交集,交集指同时出现在两个集合中的元素。不适用。Enumerable.Intersect

Queryable.Intersect

联合返回并集,并集指位于两个集合中任一集合的唯一的元素。不适用。Enumerable.Union

Queryable.Union

比较集运算

Distinct

下图演示字符序列上 Enumerable.Distinct 方法的行为。 返回的序列包含输入序列的唯一元素。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

Except

下图演示 Enumerable.Except 的行为。 返回的序列只包含位于第一个输入序列但不位于第二个输入序列的元素。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

相交

下图演示 Enumerable.Intersect 的行为。 返回的序列包含两个输入序列共有的元素。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

联合

下图演示对两个字符序列执行的联合操作。 返回的序列包含两个输入序列的唯一元素。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

03 筛选数据

筛选是指将结果集限制为仅包含满足指定条件的元素的操作。 它也称为选定内容。

下图演示了对字符序列进行筛选的结果。 筛选操作的谓词指定字符必须为“A”。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下面一节列出了执行所选内容的标准查询运算符方法。

方法

OfType根据其转换为特定类型的能力选择值。不适用。Enumerable.OfType

Queryable.OfType

Where选择基于谓词函数的值。whereEnumerable.Where

Queryable.Where

查询表达式语法示例

以下示例使用 where 子句从数组中筛选具有特定长度的字符串。

string[] words = { "the", "quick", "brown", "fox", "jumps" };

IEnumerable<string> query = from word in words
where word.Length ==
select word; foreach (string str in query)
Console.WriteLine(str); /* 输出:
the
fox
*/
04 限定符运算

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

下图描述了两个不同源序列上的两个不同限定符运算。 第一个运算询问是否有一个或多个元素为字符“A”,结果为 true。 第二个运算询问是否所有元素都为字符“A”,结果为 true

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下节列出了执行限定符运算的标准查询运算符方法。

方法

全部确定是否序列中的所有元素都满足条件。不适用。Enumerable.All

Queryable.All

任意确定序列中是否有元素满足条件。不适用。Enumerable.Any

Queryable.Any

包含确定序列是否包含指定的元素。不适用。Enumerable.Contains

Queryable.Contains

 
05 投影运算

投影是指将对象转换为一种新形式的操作,该形式通常只包含那些将随后使用的属性。 通过使用投影,您可以构造从每个对象生成的新类型。 可以投影属性,并对该属性执行数学函数。 还可以在不更改原始对象的情况下投影该对象。

下面一节列出了执行投影的标准查询运算符方法。

方法

选择投影基于转换函数的值。selectEnumerable.Select

Queryable.Select

SelectMany投影基于转换函数的值序列,然后将它们展平为一个序列。使用多个 from 子句Enumerable.SelectMany

Queryable.SelectMany

查询表达式语法示例

选择

下面的示例使用 select 子句来投影字符串列表中每个字符串的第一个字母。

List<string> words = new List<string>() { "an", "apple", "a", "day" };

var query = from word in words
select word.Substring(, ); foreach (string s in query)
Console.WriteLine(s); /* 输出:
a
a
a
d
*/

SelectMany

下面的示例使用多个 from 子句来投影字符串列表中每个字符串中的每个单词。

List<string> phrases = new List<string>() { "an apple a day", "the quick brown fox" };

var query = from phrase in phrases
from word in phrase.Split(' ')
select word; foreach (string s in query)
Console.WriteLine(s); /* 输出:
an
apple
a
day
the
quick
brown
fox
*/

Select 与 SelectMany

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

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

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

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

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

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

代码示例

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

 class Bouquet
{
public List<string> Flowers { get; set; }
} static void SelectVsSelectMany()
{
List<Bouquet> bouquets = new List<Bouquet>() {
new Bouquet { Flowers = new List<string> { "sunflower", "daisy", "daffodil", "larkspur" }},
new Bouquet { Flowers = new List<string> { "tulip", "rose", "orchid" }},
new Bouquet { Flowers = new List<string> { "gladiolis", "lily", "snapdragon", "aster", "protea" }},
new Bouquet { Flowers = new List<string> { "larkspur", "lilac", "iris", "dahlia" }}
}; // *********** Select ***********
IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers); // ********* SelectMany *********
IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers); Console.WriteLine("Results by using Select():");
// 注意这里额外的foreach循环
foreach (IEnumerable<String> collection in query1)
foreach (string item in collection)
Console.WriteLine(item); Console.WriteLine("\nResults by using SelectMany():");
foreach (string item in query2)
Console.WriteLine(item); /* 输出: Results by using Select():
sunflower
daisy
daffodil
larkspur
tulip
rose
orchid
gladiolis
lily
snapdragon
aster
protea
larkspur
lilac
iris
dahlia Results by using SelectMany():
sunflower
daisy
daffodil
larkspur
tulip
rose
orchid
gladiolis
lily
snapdragon
aster
protea
larkspur
lilac
iris
dahlia
*/ }
06 数据分区

LINQ 中的分区是指将输入序列划分为两个部分的操作,无需重新排列元素,然后返回其中一个部分。

下图显示对字符序列进行三种不同的分区操作的结果。 第一个操作返回序列中的前三个元素。 第二个操作跳过前三个元素,返回剩余元素。 第三个操作跳过序列中的前两个元素,返回接下来的三个元素。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下面一节列出了对序列进行分区的标准查询运算符方法。

运算符

Skip跳过序列中指定位置之前的元素。不适用。Enumerable.Skip

Queryable.Skip

SkipWhile基于谓词函数跳过元素,直到元素不符合条件。不适用。Enumerable.SkipWhile

Queryable.SkipWhile

Take获取序列中指定位置之前的元素。不适用。Enumerable.Take

Queryable.Take

TakeWhile基于谓词函数获取元素,直到元素不符合条件。不适用。Enumerable.TakeWhile

Queryable.TakeWhile

 
07 联接运算

联接两个数据源就是将一个数据源中的对象与另一个数据源中具有相同公共属性的对象相关联。

当查询所面向的数据源相互之间具有无法直接领会的关系时,联接就成为一项重要的运算。在面向对象的编程中,这可能意味着在未建模对象之间进行关联,例如对单向关系进行反向推理。 下面是单向关系的一个示例:Customer 类有一个类型为 City 的属性,但 City 类没有作为 Customer 对象集合的属性。 如果你具有一个 City 对象列表,并且要查找每个城市中的所有客户,则可以使用联接运算完成此项查找。

LINQ 框架中提供的 join 方法包括 Join 和 GroupJoin。 这些方法执行同等联接,即根据 2 个数据源的键是否相等来匹配这 2 个数据源的联接。 (与此相较,Transact-SQL 支持除“等于”之外的联接运算符,例如“小于”运算符。)用关系数据库术语表达,就是说 Join 实现了内部联接,这种联接只返回那些在另一个数据集中具有匹配项的对象。 GroupJoin 方法在关系数据库术语中没有直接等效项,但实现了内部联接和左外部联接的超集。 左外部联接是指返回第一个(左侧)数据源的每个元素的联接,即使其他数据源中没有关联元素。

下图显示了一个概念性视图,其中包含两个集合以及这两个集合中的包含在内部联接或左外部联接中的元素。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

方法

联接根据键选择器函数联接两个序列并提取值对。join … in … on … equals …Enumerable.Join

Queryable.Join

GroupJoin根据键选择器函数联接两个序列,并对每个元素的结果匹配项进行分组。join … in … on … equals … into …Enumerable.GroupJoin

Queryable.GroupJoin

其他技术请参阅

 
08 数据分组

分组是指将数据分到不同的组,使每组中的元素拥有公共的属性。

下图演示了对字符序列进行分组的结果。 每个组的键是字符。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下一节列出了对数据元素进行分组的标准查询运算符方法。

方法

GroupBy对共享通用属性的元素进行分组。 每组由一个 IGrouping<TKey,TElement> 对象表示。group … by

group … by … into …

Enumerable.GroupBy

Queryable.GroupBy

ToLookup将元素插入基于键选择器函数的 Lookup<TKey,TElement>(一种一对多字典)。不适用。Enumerable.ToLookup

查询表达式语法示例

下列代码示例根据奇偶性,使用 group by 子句对列表中的整数进行分组。

List<int> numbers = new List<int>() { , , , , , , , , ,  };

IEnumerable<IGrouping<int, int>> query = from number in numbers
group number by number % ; foreach (var group in query)
{
Console.WriteLine(group.Key == ? "\nEven numbers:" : "\nOdd numbers:");
foreach (int i in group)
Console.WriteLine(i);
} /* 输出:
Odd numbers:
35
3987
199
329 Even numbers:
44
200
84
4
446
208
*/

其他技术请参阅

09 生成运算

生成是指创建新的值序列。

下面一节列出了执行生成的标准查询运算符方法。

方法

DefaultIfEmpty用默认值单一实例集合替换空集合。不适用。Enumerable.DefaultIfEmpty

Queryable.DefaultIfEmpty

返回一个空集合。不适用。Enumerable.Empty
范围生成包含数字序列的集合。不适用。Enumerable.Range
Repeat生成包含一个重复值的集合。不适用。Enumerable.Repeat
 
10 相等运算

两个序列,其相应元素相等且具有被视为相等的相同数量的元素。

方法

SequenceEqual通过以成对方式比较元素确定两个序列是否相等。不适用。Enumerable.SequenceEqual

Queryable.SequenceEqual

 
11 元素运算

元素运算从序列中返回唯一、特定的元素。

下节列出了执行元素运算的标准查询运算符方法。

方法

ElementAt返回集合中指定索引处的元素。不适用。Enumerable.ElementAt

Queryable.ElementAt

ElementAtOrDefault返回集合中指定索引处的元素;如果索引超出范围,则返回默认值。不适用。Enumerable.ElementAtOrDefault

Queryable.ElementAtOrDefault

First返回集合的第一个元素或满足条件的第一个元素。不适用。Enumerable.First

Queryable.First

FirstOrDefault返回集合的第一个元素或满足条件的第一个元素。 如果此类元素不存在,则返回默认值。不适用。Enumerable.FirstOrDefault

Queryable.FirstOrDefault

Queryable.FirstOrDefault<TSource>(IQueryable<TSource>)

上一个返回集合的最后一个元素或满足条件的最后一个元素。不适用。Enumerable.Last

Queryable.Last

LastOrDefault返回集合的最后一个元素或满足条件的最后一个元素。如果此类元素不存在,则返回默认值。不适用。Enumerable.LastOrDefault

Queryable.LastOrDefault

Single返回集合的唯一一个元素或满足条件的唯一一个元素。如果没有要返回的元素或要返回多个元素,则引发 InvalidOperationException不适用。Enumerable.Single

Queryable.Single

SingleOrDefault返回集合的唯一一个元素或满足条件的唯一一个元素。如果没有要返回的元素,则返回默认值。 如果要返回多个元素,则引发 InvalidOperationException不适用。Enumerable.SingleOrDefault

Queryable.SingleOrDefault

 
12 数据类型转换

转换方法可更改输入对象的类型。

LINQ 查询中的转换运算可用于各种应用程序。 以下是一些示例:

方法

下表列出了执行数据类型转换的标准查询运算符方法。

本表中名称以“As”开头的转换方法可更改源集合的静态类型,但不对其进行枚举。 名称以“To”开头的方法可枚举源集合,并将项放入相应的集合类型。

AsEnumerable返回类型化为 IEnumerable<T> 的输入。不适用。Enumerable.AsEnumerable
AsQueryable将(泛型)IEnumerable 转换为(泛型)IQueryable不适用。Queryable.AsQueryable
Cast将集合中的元素转换为指定类型。使用显式类型化的范围变量。 例如:

from string str in words

Enumerable.Cast

Queryable.Cast

OfType根据其转换为指定类型的能力筛选值。不适用。Enumerable.OfType

Queryable.OfType

ToArray将集合转换为数组。 此方法强制执行查询。不适用。Enumerable.ToArray
ToDictionary根据键选择器函数将元素放入 Dictionary<TKey,TValue>。 此方法强制执行查询。不适用。Enumerable.ToDictionary
ToList将集合转换为 List<T>。 此方法强制执行查询。不适用。Enumerable.ToList
ToLookup根据键选择器函数将元素放入 Lookup<TKey,TElement>(一对多字典)。 此方法强制执行查询。不适用。Enumerable.ToLookup

查询表达式语法示例

下面的代码示例使用显式类型化的范围变量将类型转换为子类型,然后才访问仅在此子类型上可用的成员。

 class Plant
{
public string Name { get; set; }
} class CarnivorousPlant : Plant
{
public string TrapType { get; set; }
} static void Cast()
{
Plant[] plants = new Plant[] {
new CarnivorousPlant { Name = "Venus Fly Trap", TrapType = "Snap Trap" },
new CarnivorousPlant { Name = "Pitcher Plant", TrapType = "Pitfall Trap" },
new CarnivorousPlant { Name = "Sundew", TrapType = "Flypaper Trap" },
new CarnivorousPlant { Name = "Waterwheel Plant", TrapType = "Snap Trap" }
}; var query = from CarnivorousPlant cPlant in plants
where cPlant.TrapType == "Snap Trap"
select cPlant; foreach (Plant plant in query)
Console.WriteLine(plant.Name); /* 输出: Venus Fly Trap
Waterwheel Plant
*/
}
13 串联运算

串联是指将一个序列附加到另一个序列的操作。

下图描绘了两个字符序列的串联操作。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下面一节列出了执行串联的标准查询运算符方法。

方法

Concat连接两个序列以组成一个序列。不适用。Enumerable.Concat

Queryable.Concat

 
14 聚合运算

聚合运算从值的集合中计算出单个值。 例如,从一个月累计的每日温度值计算出日平均温度值就是一个聚合运算。

下图显示对数字序列进行两种不同聚合操作所得结果。 第一个操作累加数字。 第二个操作返回序列中的最大值。

C#3.0新增功能09 LINQ 标准查询运算符 04 运算-LMLPHP

下节列出了执行聚合运算的标准查询运算符方法。

方法

聚合对集合的值执行自定义聚合运算。不适用。Enumerable.Aggregate

Queryable.Aggregate

平均值计算值集合的平均值。不适用。Enumerable.Average

Queryable.Average

计数对集合中元素计数,可选择仅对满足谓词函数的元素计数。不适用。Enumerable.Count

Queryable.Count

LongCount对大型集合中元素计数,可选择仅对满足谓词函数的元素计数。不适用。Enumerable.LongCount

Queryable.LongCount

最大值确定集合中的最大值。不适用。Enumerable.Max

Queryable.Max

最小值确定集合中的最小值。不适用。Enumerable.Min

Queryable.Min

Sum对集合中的值求和。不适用。Enumerable.Sum

Queryable.Sum

 
04-15 16:02