标准查询运算符

标准查询运算符是组成 LINQ 模式的方法。 这些方法中的大多数都作用于序列;其中序列指其类型实现 IEnumerable 接口或 IQueryable 接口的对象。 标准查询运算符提供包括筛选、投影、聚合、排序等在内的查询功能。

共有两组 LINQ 标准查询运算符,一组作用于类型 IEnumerable 的对象,另一组作用于类型 IQueryable 的对象。

构成每个集合的方法分别是 Enumerable 和 Queryable 类的静态成员。 这些方法被定义为作为方法运行目标的类型的扩展方法。

各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。 返回单一实例值的这些方法(例如 Average 和 Sum)立即执行。 返回序列的方法会延迟查询执行,并返回一个可枚举的对象。

对于在内存中集合上运行的方法(即扩展 IEnumerable 的那些方法),返回的可枚举对象将捕获传递到方法的参数。 在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。

相反,扩展 IQueryable 的方法不会实现任何查询行为。 它们生成一个表示要执行的查询的表达式树。 源 IQueryable 对象执行查询处理。

查询语法和方法语法在语义上是相同的,但是许多人发现查询语法更简单且更易于阅读。 某些查询必须表示为方法调用。

下面的示例演示一个简单查询表达式以及编写为基于方法的查询的语义上等效的查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class QueryVMethodSyntax
{
static void Main()
{
int[] numbers = { 5, 10, 8, 3, 6, 12};

//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;

//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}

// Keep the console open in debug mode.
Console.WriteLine(System.Environment.NewLine);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
/*
Output:
6 8 10 12
6 8 10 12
*/

这两个示例的输出是相同的。 可以看到查询变量的类型在两种形式中是相同的:IEnumerable

标准查询运算符的查询表达式语法

方法 查询表达式
Cast 使用显式类型化范围变量,例如:from int i in numbers
GroupBy group … by - 或 - group … by … into
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable, IEnumerable, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable, TResult>) join … in … on … equals … into …
Join<TOuter,TInner,TKey,TResult>(IEnumerable, IEnumerable, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …
OrderBy<TSource,TKey>(IEnumerable, Func<TSource,TKey>) orderby
OrderByDescending<TSource,TKey>(IEnumerable, Func<TSource,TKey>) orderby … descending
Select select
SelectMany 多个 from 子句
ThenBy<TSource,TKey>(IOrderedEnumerable, Func<TSource,TKey>) orderby …, …
ThenByDescending<TSource,TKey>(IOrderedEnumerable, Func<TSource,TKey>) orderby …, … descending
Where where

标准查询运算符按执行方式的分类

标准查询运算符方法的 LINQ to Objects 实现主要通过两种方法之一执行:立即执行和延迟执行。 使用延迟执行的查询运算符可以进一步分为两种类别:流式处理和非流式处理。

即时

立即执行指的是读取数据源并执行一次运算。 返回标量结果的所有标准查询运算符都立即执行。

可以使用 Enumerable.ToList 或 Enumerable.ToArray 方法强制查询立即执行。

立即执行可重用查询结果,而不是查询声明。 结果被检索一次,然后存储以供将来使用。

推迟

延迟执行指的是不在代码中声明查询的位置执行运算。 仅当对查询变量进行枚举时才执行运算,例如通过使用 foreach 语句执行。

这意味着,查询的执行结果取决于执行查询而非定义查询时的数据源内容。

如果多次枚举查询变量,则每次结果可能都不同。

几乎所有返回类型为 IEnumerable 或 IOrderedEnumerable 的标准查询运算符皆以延迟方式执行。

延迟执行提供了查询重用功能,因为在每次循环访问查询结果时,查询都会从数据源中提取更新的数据。

流式处理

流式处理运算符不需要在生成元素前读取所有源数据。 在执行时,流式处理运算符一边读取每个源元素,一边对该源元素执行运算,并在可行时生成元素。

流式处理运算符将持续读取源元素直到可以生成结果元素。 这意味着可能要读取多个源元素才能生成一个结果元素。

非流式处理

非流式处理运算符必须先读取所有源数据,然后才能生成结果元素。

排序或分组等运算均属于此类别。 在执行时,非流式处理查询运算符将读取所有源数据,将其放入数据结构,执行运算,然后生成结果元素。

分类表

标准查询运算符 返回类型 立即执行 延迟的流式处理执行 延迟非流式处理执行
Aggregate TSource X
All Boolen X
Any Boolen X
AsEnumerable IEnumerable<T> X
Average 单个数值 X
Cast IEnumerable<T> X
Concat IEnumerable<T> X
Contains Boolen X
Count Int32 X
DefaultIfEmpty IEnumerable<T> X
Distinct IEnumerable<T> X
ElementAt TSource X
ElementAtOrDefault TSource X
Empty TSource X
Except TSource X X
First TSource X
FirstOrDefault TSource X
GroupBy TSource X
GroupJoin TSource X X

Aggregate

1
public static TResult Aggregate<TSource,TAccumulate,TResult> (this System.Collections.Generic.IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate,TSource,TAccumulate> func, Func<TAccumulate,TResult> resultSelector);

对序列应用累加器函数。 将指定的种子值用作累加器的初始值,并使用指定的函数选择结果值。

1
public static TAccumulate Aggregate<TSource,TAccumulate> (this System.Collections.Generic.IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate,TSource,TAccumulate> func);

对序列应用累加器函数。 将指定的种子值用作累加器初始值。

1
public static TSource Aggregate<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,TSource,TSource> func);

对序列应用累加器函数。

ALL

1
public static bool All<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool> predicate);

确定序列中的所有元素是否都满足条件。如果源序列中的每个元素都通过指定谓词中的测试,或者序列为空,则为 true;否则为 false。

Any

1
public static bool Any<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);

确定序列是否包含任何元素。
此方法不返回集合中的任何一个元素, 而是确定集合是否包含任何元素。

一旦可以确定结果,就会停止枚举 source 。

1
public static bool Any<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool> predicate);

确定序列中是否存在任意一个元素满足条件。

如果源序列不为空,并且至少有一个元素通过指定谓词中的测试,则为 true;否则为 false。

AsEnumerable

1
public static System.Collections.Generic.IEnumerable<TSource> AsEnumerable<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);

返回类型化为 IEnumerable<T> 的输入。

Average

1
public static float Average (this System.Collections.Generic.IEnumerable<float> source);

计算 Single 值序列的平均值。

1
public static double? Average<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,int?> selector);

计算可以为 null 的 Int32 值序列的平均值,这些值可通过对输入序列的每个元素调用转换函数获得。

如果源序列为空或仅包含为 null 的值,则为null;否则为值序列的平均值。

1
2
3
4
5
6
7
8
9
string[] fruits = { "apple", "banana", "mango", "orange", "passionfruit", "grape" };

double average = fruits.Average(s => s.Length);

Console.WriteLine("The average string length is {0}.", average);

// This code produces the following output:
//
// The average string length is 6.5.

Cast

1
2
public static System.Collections.Generic.IEnumerable<TResult> Cast<TResult> (this System.Collections.IEnumerable source);

将 IEnumerable 的元素强制转换为指定的类型。

Concat

连接两个序列。

ElementAtOrDefault

返回序列中指定索引处的元素;如果索引超出范围,则返回默认值。

Empty

返回具有指定类型参数的空 IEnumerable<T>

Except

生成两个序列的差集。

First

返回序列中的第一个元素。

FirstOrDefault

返回序列中的第一个元素;如果未找到该元素,则返回默认值。

GroupBy

1
public static System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,TKey> keySelector, Func<TSource,TElement> elementSelector, Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);

根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。 通过使用指定的函数对每个组的元素进行投影。

1
GroupBy<TSource,TKey,TElement,TResult>(IEnumerable<TSource>, Func<TSource, TKey>, Func<TSource,TElement>, Func<TKey,IEnumerable<TElement>, TResult>, IEqualityComparer<TKey>)

GroupJoin

1
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>, IEqualityComparer<TKey>)

基于键值等同性对两个序列的元素进行关联,并对结果进行分组。使用指定的 IEqualityComparer<T> 对键进行比较。