原文:《C# Version 3.0 Specification》,Microsoft
翻譯:lover_P
查詢表達式(Query Expression)為查詢提供了一種語言集成的語法,這種語法類似於關系和分級查詢語言,如SQL和XQuery。
query-expression:
from-clause query-body
from-clause:
from from-generators
from-generators:
from-generator
from-generators , from-generator
from-generator:
identifIEr in expression
query-body:
from-or-where-clausesopt orderby-caluseopt select-or-group-clause into-clauSEOpt
from-or-where-clauses:
from-or-where-clause
from-or-where-clauses from-or-where-clause
from-or-where-clause:
from-clause
where-clause
where-clause:
where boolean-expression
orderby-clause:
orderby ordering-clauses
ordering-clauses:
ordering-clause
ordering-clauses , ordering-clause
ordering-clause:
expression ordering-directionopt
ordering-direction:
ascending
descending
select-or-group-clause:
select-clause
group-clause
select-clause:
selelct expression
group-clause:
group expression by expression
into-clause:
into identifIEr query-body
一個查詢表達式以一個from子句開始,以一個select或group子句結束。起始的from子句後可以跟零個或多個from或where子句。每個from子句都是一個生成器,該生成器引入了一個可以覆蓋整個序列的迭代變量;而每個where子句都是一個過濾器,該過濾器用於從結果中排出項目。最終的select或group子句根據迭代變量來指定結果的表現形式。select或group子句前面還可以有一個orderby子句,用以指定結果的順序。最後,可以用一個into子句通過將一個查詢的結果作為一個子查詢的生成器來“聯結”兩個查詢。
在查詢表達式中,具有多個生成器的from子句嚴格等價於多個順序的只具有一個生成器的from子句。
7.1 查詢表達式的翻譯
C# 3.0語言並沒有為查詢表達式指定確切的執行語義,而是將查詢表達式翻譯為對附著於查詢表達式模式(Query Expression Pattern)的方法的調用。特別地,查詢表達式分別被翻譯為對名為Where、Select、SelectMany、OrderBy、OrderByDescending、ThenBy、ThenByDescending和GroupBy的方法的調用,這些方法有著預期的簽名和返回值類型。這些方法既可以是待查詢對象的實例方法,也可以是對象外部的擴展方法。這些方法進行著實際的查詢工作。
將查詢表達式翻譯為方法調用的過程是一個語法映射過程,發生在任何類型綁定或重載抉擇的執行之前。翻譯的結果可以保證語法正確,但不一定保證產生語義正確的C#代碼。在查詢表達式翻譯之後,產生的方法調用作為一般的方法調用進行處理,這時會依次發現錯誤,如方法不存在、參數類型錯誤或對一個范型方法的類型推斷失敗等。
後面的一系列示例依次演示了查詢表達式的翻譯。在後面的某一節中給出了翻譯規則的正式描述。
7.1.1 where子句
查詢表達式中的一個where子句:
from c in customers
where c.City == "London"
select c
將被翻譯為對一個Where方法的調用,其參數為合並了迭代變量和where子句中的表達式所得到的拉姆達表達式:
customers.
Where(c => c.City == "London")
7.1.2 select子句
上面的例子演示了選擇了最內部的迭代變量的select子句是如何通過翻譯為方法調用被消除的。
一個選擇了並非最內部的迭代變量的select子句:
from c in customers
where c.City == "Longdon"
select c.Name
將被翻譯為一個Select方法調用,其參數是一個拉姆達表達式:
customers.
Where(c => c.City == "London").
Select(c => c.Name)
7.1.3 group子句
一個group子句:
from c in customers
group c.Name by c.Country
將被翻譯為對GroupBy方法的調用:
customers.
GroupBy(c => c.Country, c => c.Name)
7.1.4 orderby子句
一個orderby子句:
from c in customers
orderby c.Name
select new { c.Name, c.Phone }
將被翻譯為一個對OrderBy方法的調用,或者當指定了descending指示符時,被翻譯為一個對OrderByDescending方法的調用:
customers.
OrderBy(c => c.Name).
Select(c => new { c.Name, c.Phone })
另一個orderby子句:
from c in customers
orderby c.Country, c.Balance descending
select new { c.Name, c.Country, c.Balance }
將被翻譯為對ThenBy和ThenByDescending方法的調用:
customers.
OrderBy(c => c.Country).
ThenByDescending(c => c.Balance).
Select(c => new { c.Name, c.Country, c.Balance })
7.1.5 多重生成器
多重生成器:
from c in customers
where c.City == "London"
from o in c.Orders
where o.OrderDate.Year == 2005
select new { c.Name, o.OrderID, o.Total }
將被翻譯為對所有非最內部生成器的SelectMany方法調用:
customers.
Where(c => c.City == "London").
SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new { c.Name, o.OrderID, o.Total })
)
當多重生成器被一個orderby子句合並起來:
from c in customers, o in c.Orders
where o.OrderDate.Year == 2005
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }
一個附加的Select將被注入,用於收集排序表達式和最終的結果序列。讓OrderBy可以操作整個序列是有必要的。OrderBy之後,最終的結果將被提取出來:
customers.
SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new { k1 = o.Total, v = new { c.Name, o.OrderID, o.Total } })
).
OrderByDescending(x => x.k1).
Select(x => x.v)
7.1.6 into子句
一個into子句:
from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Group.Count() }
是嵌套查詢的一種很簡單的形式:
from g in
from c in customers
group c by c.Country
select new { Country = g.Key, CustCount = g.Group.Count() }
將被翻譯為:
customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Group.Count() })
7.2 查詢表達式模式
查詢表達式模式(Query Expression Pattern)建立了類型可以實現的方法的一套模式,用以支持查詢表達式。因為查詢表達式會被通過語法映射來翻譯為方法調用,因此類型在如何實現其查詢表達式模式上尤為靈活。例如,模式的這些方法可以被實現為實例方法或擴展方法,因為兩者具有完全一樣的調用語法;而方法的參數也可以是委托或表達式樹,因為拉姆達表達式可以轉換為這兩者。
下面給出了支持查詢表達式模式的范型類型C<T>的推薦形式。范型類型用於演示參數和結果類型之間正確的關系,也可以將模式實現為非范型類型。
delegate R Func<A, R>(A arg);
class C<T>
{
public C<T> Where(Func<T, bool> predicate);
public C<S> Select<S>(Func<T, S> selector);
public C<S> SelectMany<S>(Func<T, C<S>> selector);
public O<T> OrderBy<K>(Func<T, K> keyExpr);
public O<T> OrderByDescending<K>(Func<T, K> keyExpr);
public C<G<K, T>> GroupBy<K>(Func<T, K> keyExpr);
public C<G<K, E>> GroupBy<K, E>(Func<T, K> keyExpr, Func<T, E> elemExpr);
}
class O<T> : C<T>
{
public O<T> ThenBy<K>(Func<T, K> keySelector);
public O<T> ThenByDescending<K>(Func<T, K> keySelector);
}
class G<K, T>
{
public K Key { get; }
public C<T> Group { get; }
}
上面的方法是用了一個范型委托類型Func<A, R>,也可以使用等價的其他委托或表達式樹類型,只要參數和結果類型之間存在正確的關系即可。
注意在推薦的C<T>和O<T>之間的關系中,要保證ThenBy和ThenByDescending方法只能用在OrderBy或OrderByDescending的結果上。同時請注意GroupBy結果的推薦形式,應該是一組具有Key和Group屬性的(匿名類型實例)序列。
標准查詢運算符(Standard Query Operators,在另外一個規范中描述)提供了查詢表達式的一個實現,這個實現可以用於所有實現了System.Collections.Generic.IEnumerable<T>接口的類型。
7.3 正式的翻譯規則
對一個查詢表達式的處理將重復、依次地應用下列翻譯規則。每個翻譯都一直應用這些規則直到不再發生任何給定的模式。
注意將會產生對OrderBy和ThenBy的調用的翻譯,如果相應的排序子句制定了descending指示符,將產生對OrderByDescending或ThenByDescending的調用。
l 包含了into子句的查詢:
q1 into x q2
將被翻譯為:
from x in (q1) q2
l 具有多個生成器的from子句:
from g1, g2, ... gn
將被翻譯為:
from g1 from g2 ... from gn
l 後面立即跟有where子句的from子句:
from x in e where f
將被翻譯為:
from x in (e).Where(x => f)
l 具有多個from子句、一個orderby子句和一個select子句的查詢表達式:
from x1 in e1 from x2 in e2 ... orderby k1, k2 ... select v
將被翻譯為:
(from x1 in e1 from x2 in e2 ...
select new { K1 = k1, K2 = k2 ..., V = v })
.OrderBy(x => x.K1).ThenBy(x => x.K2)...
.Select(x => x.V)
l 具有多個from子句、一個orderby子句和一個group子句的查詢表達式:
from x1 in e1 from x2 in e2 ... orderby k1, k2 ... group v by g
將被翻譯為:
(from x1 in e1 from x2 in e2 ...
select new { K1 = k1, K2 = k2 ..., V = v, G = g })
.OrderBy(x => x.K1).ThenBy(x => x.K2) ...
.GroupBy(x => x.G, x => x.V)
l 具有多個from子句和一個select子句的查詢表達式:
from x in e from x1 in e1 ... select v
將被翻譯為:
(e).SelectMany(x => from x1 in e1 ... select v)
l 具有多個from子句和一個group子句的查詢表達式:
from x in e from x1 in e1 ... group v by g
將被翻譯為:
(e).SelectMany(x => from x1 in e1 ... group v by g)
l 具有一個from子句、沒有orderby子句,並且具有一個select子句的查詢表達式:
from x in e select v
將被翻譯為:
(e).Select(x => v)
當v就是標識符x時,翻譯將被簡化為:
(e)
l 具有一個from子句、沒有orderby子句,並且具有一個group子句的查詢表達式:
from x in e group v by g
將被翻譯為
(e).GroupBy(x => g, x => v)
當v就是標識符x時,翻譯將被簡化為:
(e).GroupBy(x => g)
l 具有一個from子句、一個orderby子句和一個select子句的查詢表達式:
from x in e orderby k1, k2 ... select v
將被翻譯為:
(e).OrderBy(x => k1).ThenBy(x => k2) ...
.Select(x => v)
當v就是標識符x時,翻譯將被簡化為:
(e).OrderBy(x => k1).ThenBy(x => k2) ...
l 具有一個from子句、一個orderby子句和一個group子句的查詢表達式:
from x in e orderby k1, k2 ... group v by g
將被翻譯為:
(e).OrderBy(x => k1).ThenBy(x => k2) ...
.GroupBy(x => g, x => v)
當v就是標識符x時,翻譯將被簡化為:
(e).OrderBy(x => k1).ThenBy(x => k2) ...
.GroupBy(x => g)