LinQ基礎 - 延遲執行(Deferred Execution)
延遲執行的基礎 - 疊代器 & 走訪
詳細請參考下列文章.
這邊會用一個例子快速回顧 , 以下是自定義類別
以下是執行 foreach 的程式
以下是運行結果
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
使用上面例子 , 利用 Visual Studio 去逐步偵錯, 可以知道 foreach 的執行順序其實是
- 進入 foreach
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 執行 GetEnumerator() , 得到一個 IEnumerator
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 執行 MoveNext() , 以判斷走訪是否結束. 若尚未結束則將 Current 屬性移動到下一個元素.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 回傳 Current 屬性給 item (也就是 yield return value; 這一行.)
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
所以不論是 IEnumerable 或是 IEnumerable<T> 都提供一個 GetEnumerator() 方法. 再透過所得到的 Enumerator 物件去執行走訪這個動作.
延遲執行的時機
一般來說程式執行到哪一行 , 該行運算式就應該立即被執行. 但 LINQ 有一個很重要的特性 , 叫做「延遲執行」(deferred execution), 或稱為惰性求值(lazy evaluation). 顧名思義 , 就是在需要取用查詢結果的時候,才去執行查詢表示式. 請看下面範例
自定義 Where 以及 Select
測試程式 - query 會篩選 list 中大於 3 的數字並將其加一.
結果會印出 6 , 9
query 若是立即執行查詢的話 , 則結果應該是 6 , 10 , 9 才對.
所以查詢時機應該是在 foreach 那一行.
由此可以推測 LinQ 的延遲執行有兩個特性
- 建立查詢與執行查詢的時機是不同的
- 執行查詢的時機為存取 IEnumerable 中元素的時候.
延遲執行的運作過程
測試程式
原本我以為下列這行的執行順序是 Where 計算完結果後 , 在繼續執行 Select 如下圖.
但實際情況卻並非如此
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
再次使用 Visual Studio 去逐步偵錯可發現執行結果為
- 開始
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 不斷按下 F11 , 本以為會進入 GetStudent 內 ,但卻一路執行到 foreach. 原因是 students 以及 names 都是 IEnumerable<T> 型別. 在開始走訪前 , 都不會執行敘述.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 呼叫 GetEnumerator()
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 執行 MoveNext() , 這裡指的是 names 的下一個. 但有趣的是 names 的下一個是什麼!? names 其實是從 people.Where().Select() 的結果而來的. 所以要走訪 names 就需要知道 Select() 完的結果是什麼. 因為延遲執行 , 所以 names 的 MoveNext() 會呼叫 Select(). 有點 chain 的感覺.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 進入 Select() , 準備開始走訪.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 當我們在 Select() 方法中 , 呼叫 MoveNext() 時會去執行 Where() 的方法內容 , 因為 source 是 Where()的結果 , 所以想要走訪 source , 就需要取得 Where() 的結果.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 進入 Where() , 準備開始走訪.
- 同理 , where() 內的 source 是 students , 而 studnets 是來自於 GetStudent() 的結果.
- 進入 GetStudent() 內 , 並回傳第一個結果 , 小王.
- 回到 Where() , 因為小王不符合 predicate 的條件 , 因此沒進入 if 敘述內. 直接繼續執行 while(). 也就是繼續呼叫 MoveNext().
- 取得第二個結果 , 大明.
- 再次回到 Where , 並再次讓 predicate 來判斷. 大明符合條件 , 所以進入 if 區域內執行 yield return , 回傳結果.
- 回到 Select , 執行 yield retrun , 回傳 selector() 的結果.
- 回到 main , name 接收到回傳的結果.
- 印出結果大明 , 之後繼續執行 foreach , 直到 MoveNext() 回傳 false 為止
所以實際的執行順序 , 應該如下圖所示 :
結論
- 可以透過 yield 關鍵字輕易地完成延遲執行的效果.
- 走訪的動作 , 其實是透過 IEnumerator 來達成.
- IEnumerable 型別可以作為資料集合操作.
- 大部分地 LINQ to Objects API 幾乎都是針對IEnumerable<TSource> 進行擴充.
- 回傳 IEnumerable 型別代表回傳的結果可以走訪. 但卻不會立即走訪. 會直到執行 MoveNext() , 才會開始走訪到下一個. 這也解釋了設定查詢以及執行查詢的驅動時間點不同的原因. 也就是具有延遲執行的特性.
- 因為 LinQ 具有延遲執行的特性這代表
- 設定查詢式後 , 異動來源資料的內容 , 稍後取得查詢結果時是依據最後的資料集合去做查詢的.
- 撰寫一個查詢式後 , 不論要查詢結果幾次 , 都不需要重新撰寫查詢式.
- 使用 LinQ 時需要注意是否需要立刻取得即時的查詢結果. 是否介意查詢結果會隨著查詢源的操作改變而不同.
補充 : 立即執行
雖然 LinQ 的方法有些具有延遲執行的特性 , 但有些則沒有.
像是
- 轉型類型的 API
- ToList()
- ToArray()
- ToDictionary()
- ToHashSet()
- ToLookUp()
- …?
- 回傳值為單一值
- Max()
- Min()
- Count()
- First()
- Last()
- Single()
- Average()
- Sum()
- …?
Thank you!
You can find me on
若有謬誤 , 煩請告知 , 新手發帖請多包涵
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →