iT邦幫忙

DAY 17
2

分享一些學習心得系列 第 17

LINQ自學筆記-打地基-延後執行

本篇是打地基的最後一篇文章,講的是 LINQ 很重要的一個特色,延後執行。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法、泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
延後執行(Deferred Execution)是 LINQ 打地基系列的最後一篇,也是 LINQ 很重要的一個特色!

LINQ 預設使用延後查詢執行,也就是說當我們寫好 LINQ 查詢語法時,「通常」當下是不會執行取資料的行為,得等到我們真正要「接觸」查詢結果時,才會觸發取資料的行為,取回查詢結果,這也就意味著:

  1. 我們可以在取得查詢結果前,異動來源資料的內容,稍後取得查詢結果時,可取到最後異動過的資料。
  2. 我們可以撰寫一個查詢語法,先執行一次以取得異動前的資料,再異動來源資料的內容,然後再執行一次取得異動後的資料,不需重新撰寫查詢子句。

更簡單的說法就是:我們可以定義一個查詢語法,然後多次列舉查詢結果,每次列舉行為發生時,都可以取得資料來源中,查詢當下最新的資料:

//初始化資料來源
int[] intAry = new int[]{0, 1, 2};
//建立查詢
var query = from n in intAry select n;
//第一次執行查詢
query.Dump("第一次查詢-原始資料");
//修改資料來源
intAry[0] = 99;
intAry[2] = 97;
//第二次執行查詢(並未異動查詢語法)
query.Dump("第二次查詢-延後執行的效果");


但是請注意,並不是 LINQ 語法就一定是延後執行,要看語法中所應用到的「標準查詢運算子」為何。最簡單的辨識方法就是:只要是能以「查詢運算式」撰寫的運算子,都屬於延後執行的標準查詢運算子。

當然這種說法並不精確,但是卻很好了解。如果要精確的定義,其實應該是要看呼叫的查詢運算子,其所傳回的是單一值或值序列(傳回一連串值)而定。若是回傳單一值,例如:fFirst、Average、Sum、Count、Any 等等運算子,就是產生立即執行(Immediately execution)的效果:

//初始化資料來源
var list = new List<int>(){1,5,10};
//建立查詢呼叫加總(Sum)運算子
var querySum = (from n in list select n).Sum (c => c);
//第一次執行查詢
querySum.Dump("第一次查詢-原始資料加總");
//修改資料來源
list.Add(99);
//第二次執行查詢(並未異動查詢語法)
querySum.Dump("第二次查詢");
//重新定義 LINQ 查詢子句
querySum = (from n in list select n).Sum (c => c);
querySum.Dump("第三次查詢");


反之,若是回傳值序列,當然就是延後執行的運算子。(請見第一個程式碼範例)
不過觸發立即執行作業還有一種情形,就是呼叫轉換運算子,也就是ToList<TSource>、ToArray<TSource>、ToLookup 和 ToDictionary。這些運算子雖然回傳的是值序列,但是因為有型別轉換的動作,所以會觸發立即執行作業。
另外延後執行運算子,其實還分成兩類:資料流和非資料流(Streaming or Non-Streaming)。
什麼意思?很簡單,資料流指的就是每次列舉時,只取出一個項目;非資料流指就是該運算子必須先把所有資料都取出來才能產生結果,因此一旦發生列舉動作,該運算子的結果會先產出到記憶體中,等待列舉時回傳給呼叫端。

在 MSDN 有表格可以讓我們查詢那些運算子的類型(立即執行、延後資料流執行、延後非資料流執行),大家如果想了解更多,可以查詢該表:
http://msdn.microsoft.com/zh-tw/library/bb882641(v=vs.110).aspx

最後,有興趣想要了解 LINQ 延後執行底層的技術,可以欣賞小朱和 91 的文章:
小朱:http://www.dotblogs.com.tw/regionbbs/archive/2012/01/12/linq.deferred.execution.aspx
91:http://www.dotblogs.com.tw/hatelove/archive/2012/01/12/how-to-design-simple-deferred-execution-of-expression.aspx


上一篇
LINQ自學筆記-打地基-方法架構查詢
下一篇
LINQ自學筆記-語法應用-取出資料-Select 運算子
系列文
分享一些學習心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
smartleos
iT邦新手 3 級 ‧ 2012-10-11 01:46:32

文中漏了一個段落,補一下:

不過觸發立即執行作業還有一種情形,就是呼叫轉換運算子,也就是ToList<TSource>、ToArray<TSource>、ToLookup 和 ToDictionary。這些運算子雖然回傳的是值序列,但是因為有型別轉換的動作,所以會觸發立即執行作業。

我要留言

立即登入留言