天天看點

Linq之延遲加載特性

目錄

寫在前面

系列文章

延遲加載

總結

上篇文章介紹了linq中常見的幾個關鍵字,并列舉了幾個例子,算是對linq如何使用有了初步了解。上篇文章中也提到了,能夠使用linq的場合有一個要求:實作IEnumerable<T>泛型接口,或者類型相容(可以通過Cast方法轉換,比如ArrayList)。

Linq之Lambda表達式初步認識

Linq之Lambda進階

Linq之隐式類型、自動屬性、初始化器、匿名類

Linq之擴充方法

Linq之Expression初見

Linq之Expression進階

Linq之Expression進階篇(常用表達式類型)

Linq之常見關鍵字

延遲加載在很多orm架構中都有支援,什麼是延遲加載?通俗一點,就是你需要的時候再去查詢,不需要的時候就不查詢。

Linq查詢的執行結果是IEnumerable<T>類型,而對IEnumerable<T>,在内部,C#通過yield關鍵字實作疊代器達到延遲加載的目的。進而使Linq查詢隻是在需要的時候才會被執行。

下面看一個例子

1 namespace Wolfy.LinqLazyLoad
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             List<Person> persons = new List<Person>() { 
 8             new Person(){ ID=1,Name="wolfy1", Age=1},
 9              new Person(){ ID=2,Name="wolfy2", Age=2},
10               new Person(){ ID=3,Name="wolfy3", Age=3},
11                new Person(){ ID=4,Name="wolfy4", Age=4},
12                 new Person(){ ID=5,Name="wolfy5", Age=5},
13                  new Person(){ ID=6,Name="wolfy6", Age=6}
14             };
15             //這裡使用linq進行查詢
16             var query = from p in persons
17                         .OrderByDescending(p => p.Age)
18                         select new { p.ID, p.Name, p.Age };
19             //如果是linq是延遲加載的,則輸出的結果就應該是修改後的(延遲加載,說明query中此時并沒有實際加載資料)
20             //如果linq立即加載的,則此時query中就相當于一個臨時的緩沖區,資料已經存在了query中,就算對persons中某一項修改并不影響query中的資料。
21             persons[2] = new Person() { ID = 7, Name = "zhangsan", Age = 7 };
22             foreach (var item in query)
23             {
24                 Console.WriteLine(item.ToString());
25             }
26             Console.Read();
27         }
28     }
29     class Person
30     {
31         public int ID { set; get; }
32         public string Name { set; get; }
33         public int Age { set; get; }
34         public override string ToString()
35         {
36             return ID + " " + Name + " " + Age;
37         }
38     }
39 }      

例子很簡單,通過linq查詢,按年齡降序輸出。

看一下輸出結果

Linq之延遲加載特性

通過這點也許你可能還不是很清楚。

那麼我們再舉一個linq立即加載的例子,對比一下

1   static void Main(string[] args)
 2         {
 3             List<Person> persons = new List<Person>() { 
 4             new Person(){ ID=1,Name="wolfy1", Age=1},
 5              new Person(){ ID=2,Name="wolfy2", Age=2},
 6               new Person(){ ID=3,Name="wolfy3", Age=3},
 7                new Person(){ ID=4,Name="wolfy4", Age=4},
 8                 new Person(){ ID=5,Name="wolfy5", Age=5},
 9                  new Person(){ ID=6,Name="wolfy6", Age=6}
10             };
11             //使用聚合函數年齡總和
12             var result = (from p in persons
13                          select p.Age)
14                         .Sum();
15             //如果是linq是延遲加載的,則輸出的結果就應該是修改後的(延遲加載,說明query中此時并沒有實際加載資料)
16             //如果linq立即加載的,則此時query中就相當于一個臨時的緩沖區,資料已經存在了query中,就算對persons中某一項修改并不影響query中的資料。
17             persons[2] = new Person() { ID = 7, Name = "zhangsan", Age = 7 };
18             Console.WriteLine("Sum " + result);
19             Console.Read();
20         }      

輸出結果

Linq之延遲加載特性

21=1+2+3+4+5+6。這裡也說明一個問題,在linq中,一些聚合函數例如Sum,求平均值等操作會影響linq的延遲加載特性。

上面的第一個例子是延遲加載,在query中并沒有加載資料,然後你修改了persons[2]的值,你再輸出query中的每一個值的時候,此時才是真正的加載資料,而此時加載資料,persons[2]的值已經發生變化了,是以會輸出最新的persons[2]。

第二個例子中,聚合函數為什麼會影響延遲加載特性呢,其實也很好了解,比如在該例子中進行求和運算,求和運算就需要所有的值,是以就需要先将值查詢出來,然後才能求和,此時已經将結果儲存在了result中,就算你下面再修改persons[2]的值,也沒有用了。

通過上面的例子,以及Linq查詢的執行結果是IEnumerable<T>類型,而對IEnumerable<T>,在内部,C#通過yield關鍵字實作疊代器達到延遲加載的目的。進而使Linq查詢隻是在需要的時候才會被執行,可以得到這樣的結論:

1、可以自定義一個類實作泛型接口IEnumerable<T>,在疊代器塊中通過yield關鍵字,可以實作延遲加載的目的。

2、linq中使用聚合函數,将會強制查詢,将強制進行立即加載。

上面的例子,有點繞,各種緣由,需慢慢體會。

思考:為什麼yield關鍵字就能實作延遲加載的特性呢?(查找很多資料,未果)

參考文章

http://kb.cnblogs.com/page/100043/

  • 部落格位址:http://www.cnblogs.com/wolf-sun/

    部落格版權:如果文中有不妥或者錯誤的地方還望高手的你指出,以免誤人子弟。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起讨論,共同進步!

    再次感謝您耐心的讀完本篇文章。

繼續閱讀