[深入学习C#]LINQ查询表达式详解(2)——查询表达式的转换
轉(zhuǎn)載自詩人江湖老,原文地址
C#在執(zhí)行LINQ查詢表達(dá)式的時(shí)候,并不會(huì)指定其執(zhí)行語義,而是將查詢表達(dá)式轉(zhuǎn)換為遵循查詢表達(dá)式模式的方法的調(diào)用。具體而言,查詢表達(dá)式將轉(zhuǎn)換為以下名稱的調(diào)用:Where、Select、SelectMany、Join、GroupJoin、OrderBy、OrderByDescending、ThenBy、ThenByDescending、GroupBy、Cast等等。
如同在前文中提到的用擴(kuò)展方法和Lambda表達(dá)式簡(jiǎn)化LINQ查詢表達(dá)式一般,這也是對(duì)查詢表達(dá)式的一種轉(zhuǎn)換。簡(jiǎn)化后的方法其實(shí)就是LINQ查詢的實(shí)際執(zhí)行。
本文中用來示例的代碼,參數(shù)設(shè)定都沿用上一篇文章的設(shè)定。
轉(zhuǎn)換規(guī)則簡(jiǎn)述
從查詢表達(dá)式到方法調(diào)用的轉(zhuǎn)換是一種句法映射,在執(zhí)行任何類型綁定或重載決策之前發(fā)生。該轉(zhuǎn)換可以保證在句法上正確,但不能保證生成語法正確的 C# 代碼。轉(zhuǎn)換查詢表達(dá)式后,以常規(guī)方法調(diào)用的方式處理生成的方法調(diào)用,而這進(jìn)而可能暴露錯(cuò)誤,例如在方法不存在、參數(shù)類型錯(cuò)誤或方法為泛型方法且類型推斷失敗這些情況下。
不允許對(duì)查詢表達(dá)式中的范圍變量進(jìn)行賦值。但允許 C# 實(shí)現(xiàn)在某些時(shí)候以不實(shí)施此限制,因?yàn)閷?duì)于此處介紹的句法轉(zhuǎn)換方案,有些時(shí)候可能根本無法實(shí)施此限制。
某些轉(zhuǎn)換使用由 * 指示的透明標(biāo)識(shí)符注入范圍變量。
轉(zhuǎn)換規(guī)則講解
帶繼續(xù)符的select和groupby子句的查詢表達(dá)式的轉(zhuǎn)換
繼續(xù)符是指 into 操作符,帶有繼續(xù)符的查詢表達(dá)式類似如下:
from ···into x ···
轉(zhuǎn)換為
from x in (from ···) ···
示例:
轉(zhuǎn)換為
from g infrom c in customersgroup c by c.Country select new { Country = g.Key, CustCount = g.Count() }最終轉(zhuǎn)換為
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })含有顯式范圍變量類型的查詢表達(dá)式的轉(zhuǎn)換
顯式指定范圍變量類型的 from 子句
from T x in e
轉(zhuǎn)換為
from x in (e).Cast<T>()
顯式指定范圍變量類型的 join 子句
join T x in e on k1 equals k2
轉(zhuǎn)換為
join x in (e).Cast<T>()
示例:
from Customer in customers where c.City == "London" select c轉(zhuǎn)換為
from c in customers.Cast<Customer>() where c.City == "London" select c最終轉(zhuǎn)換為
customers.Cast<Customer>().Where(c=>c.City=="London")> 顯式范圍變量類型對(duì)于查詢實(shí)現(xiàn)非泛型 IEnumerable 接口的集合很有用,但對(duì)于實(shí)現(xiàn)泛型IEnumerable 接口的集合沒什么用處。如果 customers 屬于 ArrayList 類型,則在面的示例中即會(huì)如此。
退化查詢表達(dá)式的轉(zhuǎn)換
退化查詢表達(dá)式,是指選擇源元素本身的查詢,如:
from c in customers select c確保查詢表達(dá)式的結(jié)果永不為源對(duì)象本身非常重要,因?yàn)檫@樣會(huì)向查詢的客戶端暴露源的類型和標(biāo)識(shí)符。因此,在查詢表達(dá)式為退化查詢的時(shí)候,可通過在源上顯式調(diào)用 Select 來保護(hù)直接以源代碼形式寫入的簡(jiǎn)并查詢。然后,由 Select 實(shí)施者及其他查詢操作員確保這些方法永遠(yuǎn)不會(huì)返回源對(duì)象本身。
退化查詢表達(dá)式如下:
from x in e select x
轉(zhuǎn)換為
(e).Select(x=>x)
示例:
轉(zhuǎn)換為
customers.Select(c => c)from、 let、 where、 join 和 orderby 子句的轉(zhuǎn)換
轉(zhuǎn)換規(guī)則
帶有另一個(gè) from 子句且后接一個(gè) select 子句的查詢表達(dá)式
from x1 in e1
from x2 in e2
select v
轉(zhuǎn)換為
(e1).SelectMany( x1 => e2 , ( x1 , x2 ) => v )
帶有另一個(gè) from 子句且后接一個(gè) select 子句的查詢表達(dá)式
from x1 in e1
from x2 in e2
···
轉(zhuǎn)換為
from * in (e1).SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 })
帶有 let 子句的查詢表達(dá)式
from x in e
let y=f
轉(zhuǎn)換為
from * in (e).Select( x => new { x , y = f })
帶有 where 子句的查詢表達(dá)式
from x in e
where f
···
轉(zhuǎn)換為
from x in (e).Where( x => f )
帶有 join 子句(不含 into)且后接 select 子句的查詢表達(dá)式
from x1 in e1
join x2 in e2 on k1 equals k2
select v
轉(zhuǎn)換為
( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v )
帶有 join 子句(不含 into)且后接除 select 子句之外的其他內(nèi)容的查詢表達(dá)式
from x1 in e1
join x2 in e2 on k1 equals k2
…
轉(zhuǎn)換為
from * in ( e1 ) . Join(
e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 })
…
帶有 join 子句(含 into)且后接 select 子句的查詢表達(dá)式
from x1 in e1
join x2 in e2 on k1 equals k2 into g
select v
轉(zhuǎn)換為
( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v )
帶有 join 子句(含 into)且后接除 select 子句之外的其他內(nèi)容的查詢表達(dá)式
from x1 in e1
join x2 in e2 on k1 equals k2 into g
…
轉(zhuǎn)換為
from * in ( e1 ) . GroupJoin(
e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g })
…
帶有 orderby 子句的查詢表達(dá)式
from x in e
orderby k1 , k2 , … , kn
…
轉(zhuǎn)換為
from x in ( e ) .
OrderBy ( x => k1 ) .
ThenBy ( x => k2 ).
… .
ThenBy ( x => kn )
…
如果排序子句指定 descending 方向指示器,則將改為生成對(duì) OrderByDescending 或
ThenByDescending 的調(diào)用。
轉(zhuǎn)換規(guī)則實(shí)例演示
我們假定,在下面的每個(gè)查詢表達(dá)式中沒有 let、 where、 join 或 orderby 子句,并且最多只有一個(gè)初始 from 子句。
示例1:
轉(zhuǎn)換為
customers. SelectMany(c => c.Orders,(c,o) => new { c.Name, o.OrderID, o.Total })- 1
- 2
示例2:
轉(zhuǎn)換為
from * in customers.SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }最終轉(zhuǎn)換為
customers.SelectMany(c => c.Orders, (c,o) => new { c, o }).OrderByDescending(x => x.o.Total).Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })其中 x 是編譯器生成的以其他方式不可見且不可訪問的標(biāo)識(shí)符。
示例3:
轉(zhuǎn)換為
from * in orders. Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) where t >= 1000 select new { o.OrderID, Total = t }最終轉(zhuǎn)換為
orders.Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }).Where(x => x.t >= 1000).Select(x => new { x.o.OrderID, Total = x.t })其中 x 是編譯器生成的以其他方式不可見且不可訪問的標(biāo)識(shí)符。
示例4:
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }轉(zhuǎn)換為
customers.Join(orders, c => c.CustomerID, o => o.CustomerID,(c, o) => new { c.Name, o.OrderDate, o.Total })
示例5:
轉(zhuǎn)換為
from * in customers.GroupJoin(orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }) let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }最終轉(zhuǎn)換為
customers.GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,(c, co) => new { c, co }).Select(x => new { x, n = x.co.Count() }).Where(y => y.n >= 10).Select(y => new { y.x.c.Name, OrderCount = y.n) 其中 x 和 y 是編譯器生成的以其他方式不可見且不可訪問的標(biāo)識(shí)符。
示例6:
轉(zhuǎn)換為
orders.OrderBy(o => o.Customer.Name).ThenByDescending(o => o.Total)select 子句的轉(zhuǎn)換
from x in e select v
轉(zhuǎn)換為
( e ) . Select ( x => v )
當(dāng) v 為標(biāo)識(shí)符 x 時(shí),轉(zhuǎn)換僅為
( e )
Groupby 子句的轉(zhuǎn)換
from x in e group v by k
轉(zhuǎn)換為
( e ) . GroupBy ( x => k , x => v )
當(dāng) v 為標(biāo)識(shí)符 x 時(shí),轉(zhuǎn)換為
( e ) . GroupBy ( x => k )
總結(jié)
以上是生活随笔為你收集整理的[深入学习C#]LINQ查询表达式详解(2)——查询表达式的转换的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js中toFixed方法的两个坑
- 下一篇: C#中IEnumerable.OfTyp