[讀書筆記][第十一章] C# in depth
ch 11 查詢表示式和 LINQ to Objects
intro
- Language Integrated Query (LINQ), C# 3的新特性。
- 標準查詢操作符 -> 構成查詢表示式 -> 轉譯為普通c#3 程式碼 -> 編譯(推斷、過載、Lambda表示式)
- 序列:一次只能取當前的那個元素。IEnumerable
- 延遲執行和流處理:查詢表示式被建立時,不會訪問資料,而是在記憶體中生成了這個查詢的表現形式。過濾判斷通過委託例項來表示,只有在訪問結果的第一個元素的時候,才開始執行。每次只處理一個元素。
- 範圍變數 (range variable)
- 宣告式而非命令式,函數語言程式設計思想
思考
- 何時使用查詢表示式、何時使用點標記?
選擇元素: from select
from 範圍變數 in 資料來源 where 過濾 select 投影
//11-2 from user in SampleData.AllUsers select user;
轉譯=>
SampleData.AllUsers.Select(user => user);
顯式型別的範圍變數:Cast, OfType
範圍變數都可以是隱式型別。Cast,OfType將飛型別化序列轉化為強型別。 遇到不匹配型別,Cast報錯,OfType跳過。
//11-5 ArrayList list = new ArrayList { "First", "Second", "Third"}; IEnumerable<string> strings = list.Cast<string>(); list = new ArrayList { 1, "not an int", 2, 3}; IEnumerable<int> ints = list.OfType<int>();
篩選排序:where, orderby
where
//11-8 User tim = SampleData.Users.TesterTim; var query = from defect in Sample where defect.Status != Status.Closed where defect.AssignedTo = tim select defect.Summary;
- Q. 多個where,何時合併?
- A. 邏輯上關聯的條件合併在一起,邏輯上不關聯的保持獨立。這裡兩個where可以合併。
退化的查詢表示式:select item
.Select(defect => defect)
查詢表示式的結果和源資料永遠不會是同一個物件,對返回資料集的改變也不會影響到“主”資料。
orderby descending
=>轉譯=>OrderByDescending()
或者ThenByDescending()
orderby
=>轉譯=>OrderBy()
或者ThenBy()
,預設ascendingThenBy()
對之前的一個或多個排序規則起輔助作用,是定義為IOrderedEnumerable<T>
的擴充套件方法
透明識別符號: let
<CSharp 4 Specification> 7.16.2.4 From, let, where, join and orderby clauses
A query expression with a let clause
from x in e let y = f
is translated intofrom * in ( e ) . Select ( x => new { x , y = f } )
連線:join
內連線:inner join
inner = left, outer = right 內連線從某個物件導航到另一個物件。對右邊序列進行緩衝,對左邊序列進行流處理。 在SQL中,內連線通常是把某個表的外來鍵和另一個表的主鍵進行連線。
//11-12 from defect in SampleData.AllDefects join subscription in SampleData.AllSubscriptions on defect.Project equals subcription.Project select new {defect.Summary, subscription.EmailAdress};
send defect to subcripter's emailbox
=>轉譯=>
leftSequence.Join(rightSequence, leftKeySelector, rightKeySelector, resultSelector)
where
- in left sequence
from defect in SampleData.AllDefects where defect.Status == Status.Closed join subscription in SampleData.AllSubscriptions on defect.Project equals subcription.Project select new {defect.Summary, subscription.EmailAdress};
- in right sequence
from subscription in SampleData.AllSubscriptions join defect in (from defect in SampleData.AllDefects where defect.Status == Status.Closed select defect) on defect.Project equals subcription.Project select new {defect.Summary, subscription.EmailAdress};
分組連線:join into
//11-13 from defect in SampleData.AllDefects join subscription in SampleData.AllSubscriptions on defect.Project equals subcription.Project into groupedSubscriptions select new { Defect = defect, Subscriptions = groupedSubscriptions};
group join != group by: 對於分組連線來說,左邊序列和結果序列是一對一,左邊元素不匹配任何右邊元素時,嵌入序列是空的。
=>轉譯=>
.GroupJoin()
交叉連線:cross join
不存在序列間的匹配操作,結果包含了所有可能的元素對。笛卡爾積(cartesian join)
//11-15 from user in SampleData.AllUsers from project in SampleData.AllProjects select new {User = user, Project = project}
- 右邊序列依賴於左邊元素
// 11-16 from left in Enumerable.Range(1, 4) from right in Enumerable.Range(11, left) select new {Left = left, Right = right};
=>轉譯=>
.SelectMany()
分組和延續:group by, into
group by
鍵和序列的組合封裝於IGrouping<TKey, TElement> : IEnemerable<TElement>
中。
// 11-17 from defect in SampleData.AllDefects where defect.AssignedTo != null group defect by defect.AssignedTo;
=>轉譯=>
.GroupBy()
group by 的投影
//11-18 from defect in SampleData.AllDefects where defect.AssignedTo != null group defect.Summary by defect.AssignedTo;
- 所有查詢表示式要以select 或者 group by 子句來結尾。
查詢延續(query continuations)
- 把一個查詢表示式的結果用作另個一查詢表示式的初始序列
- select / group by + into 引入新的範圍變數(清除之前的範圍變數,只有在延續中宣告的範圍變數才能在後續使用)。
Q. find scope of defect, grouped and result
// 11-20 from defect in SampleData.AllDefects where defect.AssignedTo != null group defect by defect.AssignedTo into grouped select new { Assignee=grouped.Key, Count=grouped.Count() } into result orderby result.Count descending select result;
=>轉譯=>
SampleData.AllDefects .Where(defect => defect.AssignedTo != null) .GroupBy(defect => defect.AssignedTo) .Select(gouped => new { Assignee = grouped.Key, Count = grouped.Count()}) .OrderByDescending(result => result.Count);
查詢表示式和點標記:dot notation
轉譯成extension method:查詢表示式在編譯之前,先被轉譯為普通的C# (查詢操作符、點標記:Enumerable中的擴充套件方法).
查詢表示式 | 點標記 |
---|---|
- | 沒有相應查詢表示式: .Reverse(), .ToDictionary() 等 |
- | 特定過載 |
- | 自定義比較器 |
- | 清晰可讀 |
多個lambda表示式,多個呼叫:join 的鍵選擇 | - |
排序的多個優先順序:orderby f1, f2 | .OrderBy(f1).ThenBy(f2) |
// 在匿名型別中直接使用lambda表示式引數 sequence.Select((Item, Index) => new {Item, Index});