更多标准查询操作符
除了上述基本查询工具之外,许多操作符也提供了操作序列和编写查询的有用方法,从而在标准查询操作符的方便架构中为用户提供对结果的高级控制。
排序和分组
一般而言,对查询表达式的计算会导致以某种顺序生成一系列值,该顺序是底层信息源的固有顺序。要使开发人员能够显式控制这些值的生成顺序,应定义标准查询操作符来控制该顺序。这些操作符中最基本的就是 OrderBy 操作符。
OrderBy 和 OrderByDescending 操作符可应用于任何信息源,并允许用户提供可生成用于排序结果的值的键值提取函数。OrderBy 和 OrderByDescending 还接受可用于对键施加部分顺序的可选比较函数。下面我们来看一个基本示例:
string[] names = { "Burke", "Connor", "Frank", "Everett",
"Albert", "George", "Harris", "David" };
// unity sort
var s1 = names.OrderBy(s => s);
var s2 = names.OrderByDescending(s => s);
// sort by length
var s3 = names.OrderBy(s => s.Length);
var s4 = names.OrderByDescending(s => s.Length);
前两个查询表达式会生成根据字符串比较排序源成员的新序列。后两个查询会生成根据每个字符串的长度排序源成员的新序列。
要允许多个排序准则,OrderBy 和 OrderByDescending 都应该返回 SortedSequence,而不是通用的 IEnumerable。两个操作符仅在 SortedSequence 上定义,分别名为 ThenBy 和 ThenByDescending,它们将应用附加(从属)的排序准则。ThenBy/ThenByDescending 自身会返回 SortedSequence,以允许应用任何数量的 ThenBy/ThenByDescending 操作符:
string[] names = { "Burke", "Connor", "Frank", "Everett",
"Albert", "George", "Harris", "David" };
var s1 = names.OrderBy(s => s.Length).ThenBy(s => s);
在本例中,计算由 s1 引用的查询将生成以下值序列:
"Burke", "David", "Frank",
"Albert", "Connor", "George", "Harris",
"Everett"
除了 OrderBy 系列的操作符,标准查询操作符还包括 Reverse 操作符。Reverse 只枚举序列并以相反的顺序生成相同的值。与 OrderBy 不同,Reverse 在决定顺序时不考虑实际值本身,而仅仅依赖于底层源生成的值的顺序。
OrderBy 操作符可对值序列施加排序顺序。标准查询操作符还包括 GroupBy 操作符,该操作符可根据键值提取函数对值序列进行分区。GroupBy 操作符会返回一列 Grouping 值,其中每一个对应于所遇到的不同的键值。每个 Grouping 都包含键,以及映射到该键的值集合。Grouping 的公共协定如下所示:
public sealed class Grouping {
public Grouping(K key, IEnumerable group);
public Grouping();
public K Key { get; set; }
public IEnumerable Group { set; get; }
}
最简单的 GroupBy 应用程序如下所示:
string[] names = { "Albert", "Burke", "Connor", "David",
"Everett", "Frank", "George", "Harris"};
// group by length
var grouping = names.GroupBy(s => s.Length);
foreach (Grouping group in grouping) {
Console.WriteLine("Strings of length {0}", group.Key);
foreach (string value in group.Group)
Console.WriteLine(" {0}", value);
}
运行后,该程序会显示出以下结果:
Strings of length 6
Albert
Connor
George
Harris
Strings of length 5
Burke
David
Frank
Strings of length 7
Everett
Select 和 GroupBy 允许您提供用于填充组成员的投影函数。
string[] names = { "Albert", "Burke", "Connor", "David",
"Everett", "Frank", "George", "Harris"};
// group by length
var grouping = names.GroupBy(s => s.Length,
s => s[0]);
foreach (Grouping group in grouping) {
Console.WriteLine("Strings of length {0}", group.Key);
foreach (char value in group.Group)
Console.WriteLine(" {0}", value);
}
该变体会显示以下结果:
Strings of length 6
A
C
G
H
Strings of length 5
B
D
F
Strings of length 7
E
从该示例中可以注意到,投影类型不需要与源相同。在本例中,我们从字符串序列创建了整数-字符的分组。