1. 程式人生 > >[讀書筆記][第十一章] C# in depth

[讀書筆記][第十一章] 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(),預設ascending
  • ThenBy() 對之前的一個或多個排序規則起輔助作用,是定義為 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 into from * 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});

reference