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