4 其他標(biāo)準(zhǔn)查詢操作符(More Standard Query Operators)
在最上面描述的基本的查詢技巧(query facilities)中,許多操作符(a number of operators)提供有用的方式(useful ways)來操作序列(manipulating sequences)和組裝查詢(composing queries),通過使用便利(convenient)的標(biāo)準(zhǔn)查詢操作符(standard query operators)的框架(framework)讓用戶在結(jié)果(over the result)之上有很高的控制程度(high degree of control)。
4.1 排序和分組操作(Sorting and Grouping)
通常(In general),查詢表達(dá)式(query expression)的賦值(evaluation)導(dǎo)致(產(chǎn)生)(results in)的一個(gè)序列的值(sequence of values),并且以某種順序(in some order)產(chǎn)生(produced),這是基本的信息源(the underlying information sources)的固有性質(zhì)(intrinsic)。為了讓開發(fā)者清楚地控制(explicit control)數(shù)據(jù)序列產(chǎn)生的順序,所以定義了標(biāo)準(zhǔn)查詢操作符(standard query operators)來控制順序(controlling the order)。其中最基本的操作符就是 OrderBy 。
OrderBy 和OrderByDescending 操作符可以用來應(yīng)用于任何信息源(any information source),允許用戶提供一個(gè)制造出(produces)用來給結(jié)果排序(used to sort the results)的值的一個(gè)關(guān)鍵詞分解函數(shù)(key extraction function)。OrderBy 和OrderByDescending 操作符也可以接收(accept)可選的比較函數(shù)(optional comparison function),這個(gè)函數(shù)可以用來給關(guān)鍵詞(keys)的排序強(qiáng)加上(impose)一個(gè)次序的排序(partial order)。讓我們看看下面這段基礎(chǔ)的例子:

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);

前面兩個(gè)查詢表達(dá)式產(chǎn)生新的序列是基于字符串比較(based on string comparison)的對數(shù)據(jù)源成員的排序方式(based on sorting the members of the source)產(chǎn)生的。下面兩個(gè)查詢產(chǎn)生的序列是基于每個(gè)字符串長度(based on the length of each string)的對數(shù)據(jù)源成員的排序(sorting the members of the source)方式產(chǎn)生的。
為了允許多次排序的標(biāo)準(zhǔn)(multiple sort criteria),OrderBy 和 OrderByDescending 操作符都返回 SortedSequence<T> 接口 而不是通常的 IEnumerable<T>.接口。兩個(gè)只定義在 SortedSequence<T> 接口之上的操作符,被稱作 ThenBy 和 ThenByDescending,它們用來應(yīng)用一個(gè)附加的(并且次序的)的排序標(biāo)準(zhǔn)(additional (subordinate) sort criterion)。ThenBy/ThenByDescending 操作符它們自己返回 SortedSequence<T> 接口,而且允許應(yīng)用許多(any number of)次 ThenBy/ThenByDescending 操作符:

string[] names =
{ "Burke", "Connor", "Frank", "Everett",
"Albert", "George", "Harris", "David" };

var s1 = names.OrderBy(s => s.Length).ThenBy(s => s);

在這個(gè)例子中,變量 s1 所引用(referenced)的查詢的賦值(Evaluating the query)將產(chǎn)生出(yield)下面的值的序列(sequence of values):
"Burke", "David", "Frank",
"Albert", "Connor", "George", "Harris",
"Everett"

除了(In addition to)OrderBy 種類的操作符(OrderBy family of operators),標(biāo)準(zhǔn)查詢操作符(standard query operators)還包括一個(gè) Reverse 操作符。Reverse 操作符簡單地在一個(gè)序列上進(jìn)行枚舉(enumerates)操作,產(chǎn)生(yields)一個(gè)以相反順序排序(in reverse order)的包含相同成員的一組值。與 OrderBy 不同,Reverse 在決定排序(determining the order)的時(shí)候不考慮那些值自己的真實(shí)內(nèi)容(actual values themselves),而是單獨(dú)地依靠(relies solely)基本數(shù)據(jù)源(the underlying source)所產(chǎn)成的值的順序。
OrderBy 操作符影響(imposes)一組值的序列(a sequence of values)的排列順序(a sort order)。標(biāo)準(zhǔn)查詢操作符還包括 GroupBy 操作符,它影響一組值的序列的分割(partitioning),基于一個(gè)關(guān)鍵詞分解函數(shù)(a key extraction function)。GroupBy 操作符返回一個(gè)分組的值(Grouping values)的序列,一組會遇到的每一個(gè)單獨(dú)的關(guān)鍵詞的值的序列(one for each distinct key value that was encountered)。每一分組(each grouping)既包含了關(guān)鍵詞(key)又(as well as)包含了映射到(mapped to)這個(gè) key 的那一組值(the group of values)。分組(Grouping)的公開標(biāo)識(public contract)看起來像如下的代碼:

public sealed class Grouping<K, T>
{
public Grouping(K key, IEnumerable<T> group);
public Grouping();

public K Key
{ get; set; }

public IEnumerable<T> 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<int, string> group in grouping)
{
Console.WriteLine("Strings of length {0}", group.Key);

foreach (string value in group.Group)
Console.WriteLine(" {0}", value);
}

當(dāng)運(yùn)行后,此段程序?qū)⒋蛴〕鋈缦碌慕Y(jié)果:
Strings of length 6
Albert
Connor
George
Harris
Strings of length 5
Burke
David
Frank
Strings of length 7
Everett

Select 和 GroupBy 關(guān)鍵詞允許你提供一個(gè)映射函數(shù)(projection function)來移動分組的成員(populate members of the groups):

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<int, char> group in grouping)
{
Console.WriteLine("Strings of length {0}", group.Key);

foreach (char value in group.Group)
Console.WriteLine(" {0}", value);
}

這個(gè)變化(variation)將打印出如下結(jié)果:
Strings of length 6
A
C
G
H
Strings of length 5
B
D
F
Strings of length 7
E

從這個(gè)例子中需要注意的是,投影類型(projected type)不需要跟數(shù)據(jù)源一模一樣。既然這樣(In this case),我們從一個(gè)字符串的序列(a sequence of strings)創(chuàng)建了一個(gè)從整型(integers)到字符(characters)的分組。
待續(xù), 錯(cuò)誤難免,請批評指正,譯者Naven 2005-10-24