.NET深刻解析LINQ框架(一:LINQ優雅的前奏)[轉載]

.NET深刻解析LINQ框架(一:LINQ優雅的前奏)

閱讀目錄:html

  • 1.LINQ簡述
  • 2.LINQ優雅前奏的音符
    • 2.1.隱式類型 (由編輯器自動根據表達式推斷出對象的最終類型)
    • 2.2.對象初始化器 (簡化了對象的建立及初始化的過程)
    • 2.3.Lambda表達式 (對匿名方法的改進,加入了委託簽名的類型推斷並很好的與表達式樹的結合)
    • 2.4.擴展方法 (容許在不修改類型的內部代碼的狀況下爲類型添加獨立的行爲)
    • 2.5.匿名類型 (由對象初始化器推斷得出的類型,該類型在編譯後自動建立)
    • 2.6.表達式目錄樹(用數據結構表示程序邏輯代碼)
  • 3.LINQ框架的主要設計模型
    • 3.1.鏈式設計模式(以流水線般的連接方式設計系統邏輯)
    • 3.2.鏈式查詢方法(逐步加工查詢表達式中的每個工做點)
  • 4.LINQ框架的核心設計原理
    • 4.1.託管語言之上的語言(LINQ查詢表達式)
    • 4.2.託管語言構造的基礎(LINQ依附通用接口與查詢操做符對應的方法對接)
    • 4.3.深刻IEnumerable、IEnumerable<T>、Enumerable(LINQ to Object框架的入口)
    • 4.4.深刻IQueryable、IQueryable<T>、Queryable(LINQ to Provider框架的入口)
    • 4.5.LINQ針對不一樣數據源的查詢接口
  • 5.動態LINQ查詢(動態構建Expression<T>表達式樹)
  • 6.DLR動態語言運行時(基於CLR之上的動態語言運行時)

1】.LINQ簡述

LINQ簡稱語言集成查詢,設計的目的是爲了解決在.NET平臺上進行統一的數據查詢。數據庫

微軟最初的設計目的是爲了解決對象/關係映射的解決方案,經過簡單的使用相似T-SQL的語法進行數據實體的查詢和操做。不過好的東西最終都能良性的發展演化,變成了現在.NET平臺上強大的統一數據源查詢接口。編程

咱們可使用LINQ查詢內存中的對象(LINQ to Object)、數據庫(LINQ to SQL)、XML文檔(LINQ to XML),還有更多的自定義數據源。設計模式

使用LINQ查詢自定義的數據源須要藉助LINQ框架爲咱們提供的IQueryable、IQueryProvider兩個重量級接口。後面的文章將講解到,這裏先了解一下。數據結構

在 LINQ未出現以前,咱們須要掌握不少針對不一樣數據源查詢的接口技術,對於OBJECT集合咱們須要進行重複而枯燥的循環迭代。對於數據庫咱們須要使用諸 多T-SQL\PL-SQL之類的數據庫查詢語言。對於XML咱們須要使用XMLDOM編程接口或者XPATH之類的東西,須要咱們掌握的東西太多太多, 即費力又容易忘。框架

那麼LINQ是如何作到對不一樣的數據源進行統一的訪問呢?它的優雅不是一天兩天就修來的,歸根到底還得感謝C#的設計師們,是他們讓C#能如此完美的演變,最終造就LINQ的優雅。編程語言

下面咱們來經過觀察C#的每一次演化,到底在哪裏造就了LINQ的優雅前奏。編輯器

2】.LINQ優雅前奏的音符

  • 2.1.隱式類型(由編輯器自動根據表達式推斷出對象的最終類型)

隱式類型實際上是編輯器玩的語法糖而已,可是它在很大程度上方便了咱們編碼。熟悉JS的朋友對隱式類型不會陌生,可是JS中的隱式類型與這裏的C#隱式類型是有很大區別的。儘管在語法上是同樣的都是經過var關鍵字進行定義,可是彼此最終的運行效果是大相徑庭。ide

JS是基於動態類型系統設計原理設計的,而C#是基於靜態類型系統設計的,二者在設計原理上就不同,到最後的運行時更不一樣。函數式編程

這裏順便推薦一本C#方面比較深刻的書籍 《深刻解析C#》,想深刻學習C#的朋友能夠看看。這書有兩版,第二版是咱們熟悉的姚琪琳大哥翻譯的很不錯。藉此謝謝姚哥爲咱們翻譯這麼好的一本書。這本 書很詳細的講解了C#的發展史,包括不少設計的歷史淵源。來自大師的手筆,很是具備學習參考價值,不可多得的好書。

咱們經過一個簡短的小示例來快速的結束本小節。

複製代碼
 1 List<Order> OrderList = new List<Order>() 
 2             { 
 3                 new Order(){ Count=1}, 
 4                 new Order(){ Count=2}, 
 5                 new Order(){ Count=3} 
 6             }; 
 7             foreach (Order order in OrderList) 
 8             { 
 9                 Console.WriteLine(order.Count); 
10             }
複製代碼

這裏我定義了一個List<Order>對象而且初始化了幾個值,而後經過foreach迭代數據子項。其實這種寫法很正常,也很容易理解。可是從C#3起加入了var關鍵字,編輯器對var關鍵字進行了自動分析類型的支持,請看下面代碼。

複製代碼
 1 var OrderList = new List<Order>() 
 2             { 
 3                 new Order(){ Count=1}, 
 4                 new Order(){ Count=2}, 
 5                 new Order(){ Count=3} 
 6             }; 
 7             foreach (var order in OrderList) 
 8             { 
 9                 Console.WriteLine(order.Count); 
10             }
複製代碼

編輯器能夠智能的分析出咱們定義是什 麼類型,換句話說在不少時候咱們確實須要編輯器幫咱們在編譯時肯定對象類型。這在LINQ中很常見,在你編寫LINQ查詢表達式時,你人爲的去判斷對象要 返回的類型是很不現實的,可是由編譯器來自動的根據語法規則進行分析就很理想化了。因爲LINQ依賴於擴展方法,進行鏈式查詢,因此類型在編寫時是沒法確 定的。後面的文章將詳細的講解到,這裏先了解一下。

  • 2.2.對象初始化器(簡化了對象的建立及初始化的過程)

其實對象初始化器是一個簡單的語法改進,目的仍是爲了方便咱們進行對象的構造。(所謂萬事俱備只欠東風,這個東風就是LINQ的方案。因此必須得先萬事俱備才行。)

那麼對象初始化器到底有沒有多大的用處?咱們仍是先來目擊一下它的語法到底如何。

複製代碼
1 var order = new Order() { Count = 10, OrderId = "123", OrderName = "採購單" };//屬性初始化
2 
3 var OrderList = new List<Order>() 
4             { 
5                 new Order(){ Count=1, OrderId="1",OrderName="採購單"}, 
6                 new Order(){ Count=2, OrderId="2",OrderName="採購單"}, 
7                 new Order(){ Count=3, OrderId="3",OrderName="採購單"} 
8             };//集合初始化
複製代碼

注意:對象初始化器只能用在屬性、公共字段上。

屬性初始化用這種語法編寫的效果和直接用(order.Count=10;order.OrderId="123";order.OrderName="採購單";)是相等的。

集合初始化使用大括號的多行語法也很容易理解。類不具體的子對象的數據賦值是相同的。

我想對代碼有追求的朋友都會很喜歡這種語法,確實很優美。

  • 2.3.Lambda表達式(對匿名方法的改進,加入了委託簽名的類型推斷並很好的與表達式樹的結合)

我想沒有朋友對Lambda表達式陌生的,若是你對Lambda表達式陌生的也不要緊,這裏照看不誤。後面再去補習一下就好了。

在LINQ的查詢表達式中,處處都是Lambda造就的優雅。經過封裝匿名方法來達到強類型的鏈式查詢。

Lambda是函數式編程語言中的特 性,將函數很簡單的表示起來。不只在使用時方便,查找定義也很方便。在須要的時候很簡單定義就可使用了,避免了在使用委託前先定義一個方法的繁瑣。 Lambda表達式與匿名委託在語法上是有區別的,固然這二者都是對匿名函數的封裝。可是他們的出現是匿名委託早於Lambda。因此看上去仍是 Lambda顯得優雅。

下面咱們來看一個小示例,簡單的瞭解一下Lambda的使用原理,最重要的是它優於匿名委託哪裏?

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

咱們定義一個用來過濾數據的通用方法,這是個泛型方法,在使用時須要指定類型實參。方法有兩個參數,第一個是要過濾的數據集合,第二個是要進行過濾的邏輯規則封裝。

咱們看一下調用的代碼:

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

我 們這裏定義的邏輯規則是,只要大於3的我就把提取出來而且返回。很明顯這裏的(int item) => { return item > 3; }語法段就是Lambda表達式,它很方便的封裝了方法的邏輯。從這點上看Lambda明顯要比匿名委託強大不少,最重要的是它還支持泛型的類型推斷特 性。

那麼什麼是泛型的類型推斷?

其實泛型的類型推斷說簡單點就是類型實參不須要咱們顯示的指定,編輯器能夠經過分析表達式中的潛在關係自動的得出類型實參的類型。

說的有點空洞,咱們仍是看具體的代碼比較清晰。

1 int[] Number = new int[5] { 1, 2, 3, 4, 5 }; 
2 var result = Filter(Number, (int item) => { return item > 3; });

我將上面的代碼修改爲了不須要顯示指定泛型類型實參調用,這裏也是能夠的。

咱們在定義 Filter<T>泛型方法時將Func<T,bool>泛型委託中的T定義爲匿名函數的參數類型,因此在咱們使用的時候須要指 定出類型實參(int item)中的item來表示委託將要使用的類型參數形參。在編輯器看來咱們在定義泛型方法Filter時所用的泛型佔位符T也恰巧是Filter方法的 形參數據類型Func<T,bool>中使用的調用參數類型,因此這裏的語法分析規則能準確的推斷出咱們使用的同一種泛型類型實參。(這裏要記住目前IDE編輯器只支持方法調用的泛型類型推斷,也就是說其餘方面的泛型使用是不支持隱式的類型推斷,仍是須要咱們手動加上類型實參。)

這裏順便提一下關於延遲加載技術,延遲加載技術在集合類遍歷很是有用,尤爲是在LINQ中。不少時候咱們對集合的處理不是實時的,也就是說我獲取集合的數據不是一次性的,須要在我須要具體的某一個項的時候才讓我去處理關於獲取的代碼。我稍微的改動了一下Filter代碼:

複製代碼
 1 /// <summary> 
 2         /// 按照指定的邏輯過濾數據。具備延遲加載的特性。 
 3         /// </summary> 
 4         public static IEnumerable<T> FilterByYield<T>(IEnumerable<T> ObjectList, Func<T, bool> FilterFunc) 
 5         { 
 6             foreach (var item in ObjectList) 
 7             { 
 8                 if (FilterFunc(item)) 
 9                     yield return item; 
10             } 
11         }
複製代碼

這裏使用了yield關鍵字,使用它 咱們能夠在方法內部造成一個自動的狀態機結構。簡單點講也就是說系統會幫咱們自動的實現一個繼承了IEnumerable<T>接口的對象, 在以前咱們須要本身去實現迭代器接口成員,很費時費力並且性能很差。用這種方式定義的方法後,咱們只有在遍歷具體的集合時方法纔會被調用,也算是一個很大 的性能提高。

泛型類型推斷的不足之處;

固然類型推斷還存在不足的地方,這裏能夠順便參見一下咱們老趙大哥的一篇文章:「C#編譯器對泛型方法調用做類型推斷的奇怪問題」; 我在實際工做中也遇到過一個很頭疼問題,這裏順便跟你們分享一下。按照常理說我在泛型方法的形參裏面定義一個泛型的委託,他們的形參類型都是同樣的佔位 符,可是若是我使用帶有形參的方法做爲委託的參數的話是沒法進行類型推斷的,而後使用無參數的方法做爲委託參數是徹底沒有問題的。而後必須使用 Lambda表達式才能作正確的類型推斷,若是直接將帶有參數的某個方法做爲委託的參數進行傳遞是沒法進行真確的類型推斷,這裏我表示很不理解。貼出代碼 與你們討論一下這個問題。

我定義兩個方法,這兩個方法沒有什麼意義,只是一個有參數,一個沒有參數。

無參數的方法:

1 public static List<Order> GetOrderList() 
2 { 
3 return new List<Order>(); 
4 }

有參數方法:

1 public static List<Order> GetOrderListByModel(Order model) 
2 { 
3 return new List<Order>(); 
4 }

Order對象只是一個類型,這裏沒有什麼特別意義。

兩個帶有Func委託的方法,用來演示泛型的類型推斷:

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

這裏的問題是,若是我使用 GetOrderList方法做爲GetModelList<TResult>(Func<TResult> GetFunc)泛型方法的參數是沒有任何問題的,編輯器能真確的推斷出泛型的類型。可是若是我使用GetOrderListByModel做爲 GetModelList<TSource, TResult>(Func<TSource, TResult> GetFunc)重載版本的泛型方法時就不能真確的推斷出類型。其實這裏的Func中的TResult已是方法的返回類型,TSource也是方法的參 數類型,按道理是徹底能夠進行類型推斷的。但是我嘗試了不少種方式就是過不起。奇怪的是若是我使用帶有參數和返回類型的Lambda表達式做爲 GetModelList<TSource, TResult>(Func<TSource, TResult> GetFunc)方法的參數時就能正確的類型推斷。

方法調用的圖例:

在圖的第二行代碼中,就是使用纔有參數的方法調用GetModelList方法,沒法進行真確的類型推斷。

小結:按照這個分析,彷佛對於方法的泛型類型推斷只限於Lambda表達式?若是不是爲何多了參數就沒法進行類型推斷?咱們先留着這個疑問等待答案吧;

  • 2.4.擴展方法(容許在不修改類型的內部代碼的狀況下爲類型添加獨立的行爲)

擴展方法的本意在於不修改對象內部代 碼的狀況下對對象進行添加行爲。這種方便性大大提升了咱們對程序的擴展性,雖這小小的擴展性在代碼上來看不微不足道,可是若是使用巧妙的話將發揮很大的做 用。擴展方法對LINQ的支撐很是重要,不少對象本來構建與.NET2.0的框架上,LINQ是.NET3.0的技術,如何在不影響原有的對象狀況下對對 象進行添加行爲頗有挑戰。 

那麼咱們利用擴展方法就能夠無縫的嵌 入到以前的對象內部。這樣的需求在作框架設計時很常見,最爲典型的是咱們編寫了一個.NET2.0版本的DLL文件做爲客戶端程序使用,那麼咱們有須要在 服務端中對.NET2.0版本中的DLL對象加以控制。好比傳統的WINFORM框架,咱們能夠將ORM實體做爲窗體的控件數據源,讓ORM實體與窗體的 控件之間造成天然的映射,包括對賦值、設置值都很方便。可是這樣的實體通過序列化後到達服務層,而後通過檢查進入到BLL層接着進入到DAL層,這個時候 ORM框架須要使用該實體做相應的數據庫操做。那麼咱們如何使用.NET3.0的特性爲ORM添加其餘的行爲呢?若是沒有擴展方法這裏就很無賴了。有了擴 展方法咱們能夠將擴展方法構建與.NET3.0DLL中,在添加對.NET2.0DLL的友元引用,再對ORM實體進行擴展。

咱們來看一個小例子,看看擴展方法若是使用;

複製代碼
 1 public class OrderCollection 
 2 { 
 3   public  List<Order> list = new List<Order>(); 
 4 } 
 5 public class Order 
 6 { 
 7     public int Count; 
 8     public string OrderName; 
 9     public string OrderId; 
10 }
複製代碼

這裏僅僅是爲了演示,比較簡單。我定義了一個Order類和一個OrderCollection類,目前看來OrderCollection沒有任何的方法,下面咱們經過添加一個擴展方法來爲OrderCollection類添加一寫計算方法,好比彙總、求和之類的。

如何定義擴展方法?

擴展方法必須是靜態類中的靜態方法,咱們定義一個OrderCollection類的擴展方法Count。

複製代碼
1 public static class OrderExtend 
2 { 
3     public static int Count(this OrderCollection OrderCollectionObject) 
4     { 
5         return OrderCollectionObject.list.Count; 
6     } 
7 }
複製代碼

擴 展方法的第一個參數必須是this 關鍵開頭而後經跟要擴展的對象類型,而後是擴展對象在運行時的實例對象引用。若是沒有實例對象的引用我想擴展方法也毫無心識。因此這裏咱們使用Count 方法來彙總一共有多少Order對象。經過OrderCollectionObject對象引用咱們就能夠拿到實例化的OrderCollection對 象。

1 OrderCollection orderCollection = new OrderCollection(); 
2 orderCollection.Count();

還有一個須要你們注意的是,若是咱們 定義的擴展方法在另外的命名空間裏,咱們在使用的時候必定要在當前的CS代碼中應用擴展方法所在的命名空間,要否則編輯器是不會去尋找你目前在使用的對象 的擴展方法的,切忌。這裏還有一點是須要咱們注意的,當咱們在設計後期可能會被擴展方法使用的對象時須要謹慎的考慮對象成員訪問權限,若是咱們將之後可能 會被擴展方法使用的對象設計成受保護的或者私有的,那麼可能會涉及到沒法最大力度的控制。

  • 2.5.匿名類型(由對象初始化器推斷得出的類型,該類型在編譯後自動建立)

匿名類型其實也是比較好理解的,顧名思義匿名類型是沒有類型定義的類型。這種類型是由編輯器自動生成的,僅限於當前上下文使用。廢話少說了,咱們仍是看例子吧;

1 var Student1 = new { Name = "王清培", Age = 24, Sex = "", Address = "江蘇淮安" }; 
2 var Student2 = new { Name = "陳玉和", Age = 23, Sex = "", Address = "江蘇鹽城" };

定義匿名類型跟普通的定義類型差很少,只不過在new以後是一對大括號,而後經跟着你須要使用到的屬性名稱和值。

匿名類型的做用域;

匿名類型在使用上是有它先天性缺點 的,因爲缺少顯示的類型定義,因此沒法在方法之間傳遞匿名類型。要想獲取匿名類型的各屬性值只能經過反射的方式動態的獲取運行時的屬性對象,而後經過屬性 對象去獲取到屬性的值。匿名類型在使用的時候纔會被建立類型,因此它在運行時存在着完整的對象定義元數據,因此經過反射獲取數據是徹底能夠理解的。

下面咱們使用上面定義的類型來獲取它的各個屬性。

複製代碼
 1 PrintObjectProperty(Student1, Student2);
 2 
 3 public static void PrintObjectProperty(params object[] varobject) 
 4 { 
 5     foreach (object obj in varobject) 
 6     { 
 7         foreach (System.Reflection.PropertyInfo property in obj.GetType().GetProperties()) 
 8         { 
 9             Console.WriteLine(string.Format("PropertyName:{0},PropertyValue:{1}", 
10                 property.Name, property.GetValue(obj, null))); 
11         } 
12     } 
13 }
複製代碼

圖例:

經過反射的方式咱們就能夠順利的獲取到匿名類型的屬性成員,而後經過屬性信息在順利的獲取到屬性的值。

  • 2.6.表達式目錄樹(用數據結構表示邏輯代碼)

表達式目錄樹是LINQ中的重中之 重,優雅其實就體如今這裏。咱們從匿名委託到Lambda拉姆達表達式在到如今的目錄樹,咱們看到了.NET平臺上的語言愈來愈強大。咱們沒有理由不去接 受它的美。那麼表達式目錄樹究竟是啥東西,它的存在是爲了解決什麼樣的問題又或者是爲了什麼需求而存在的?

咱們上面已經講解過關於Lambda 表示式的概念,它是匿名函數的優雅編寫方式。在Lambda表達式裏面是關於程序邏輯的代碼,這些代碼通過編譯器編譯後就造成程序的運行時路徑,根本沒法 做爲數據結構在程序中進行操做。好比在Lambda表達式裏面我編寫了這樣一段代碼 :(Student Stu)=>Stu.Name=="王清培",那麼這段代碼通過編譯器編譯後就變成了你們耳熟能詳的微軟中間語言IL。那麼在不少時候咱們須要將它 的運行特性表現爲數據結果,咱們須要人爲的去解析它,而且轉變爲另一種語言或者調用方式。那麼爲何在程序裏面須要這樣的畫蛇添足,不能用字符串的方式 表達Lambda表達式等價的表達方式呢?這樣的目的是爲了保證強類型的操做,不會致使在編譯時沒法檢查出的錯誤。而若是咱們使用字符串的方式來表達邏輯 的結構,那麼咱們只能在運行時才能知道它的正確性,這樣的正確性是很脆弱的,不知道在什麼樣的狀況下會出現問題。因此若是有了強類型的運行時檢查咱們就可 以放心的使用Lambda這樣的表達式,而後在須要的時候將它解析成各類各樣的邏輯等式。

在.NET3.5框架的 System.Linq.Expression命名空間中引入了以Expression抽象類爲表明的一羣用來表示表達式樹的子對象集。這羣對象集目的就 是爲了在運行時充分的表示邏輯表達式的數據含義,讓咱們能夠很方便的獲取和解析這中數據結構。爲了讓普通的Lambda表達式能被解析成 Expression對象集數據結構,必須得藉助Expression<T>泛型類型,該類型派生自LambdaExpression,它表 示Lambda類型的表達式。經過將Delegate委託類型的對象做爲Expression<T>中的類型形參,編輯器會自動的將 Lambda表達式轉換成Expression表達式目錄樹數據結構。咱們看來例子;

1 Func<int> Func = () => 10; 
2 Expression<Func<int>> Expression = () => 10;

編輯器對上述兩行代碼各採用了不一樣的處理方式,請看跟蹤對象狀態。

不使用Expression<T>做爲委託類型的包裝的話,該類型將是普通的委託類型。

若是使用了 Expression<T>做爲委託類型的包裝的話,編譯器將把它解析成繼承自 System.Linq.Expression.LambdaExpression類型的對象。一旦變成對象,那麼一切就好辦多了,咱們能夠經過很簡單的 方式獲取到Expression內部的數據結構。

表達式目錄樹的對象模型;

上面簡單的介紹了一下表達式目錄樹的用意和基本的原理,那麼表達式目錄樹的繼承關係或者說它的對象模型是什麼樣子的?咱們只有理清了它的總體結構這樣才能方便咱們之後對它進行使用和擴展。

下面咱們來分析一下它的內部結構。

(Student stu)=>stu.Name=="王清培",我定義了一個Lambda表達式,咱們能夠視它爲一個總體的表達式。什麼叫總體的表達式,就是說徹底 能夠用一個表達式對象來表示它,這裏就是咱們的LambdaExpression對象。表達式目錄樹的本質是用對象來表達代碼的邏輯結構,那麼對於一個完 整的Lambda表達式咱們必須可以將它徹底的拆開纔可以進行分析,那麼能夠將Lambda表達式拆分紅兩部分,而後再分別對上一次拆開的兩部分繼續拆 分,這樣遞歸的拆下去就天然而然的造成一顆表達式目錄樹,其實也就是數據結構裏面的樹形結構。那麼在C#裏面咱們很容易的構造出一個樹形結構,並且這顆樹 充滿着多態。

(Student stu)=>stu.Name="王清培",是一個什麼樣子的樹形結構呢?咱們來看一下它的運行時樹形結構,而後在展開抽象的繼承圖看一下它是如何構造出來的。

上圖中的第一個對象是 Expression<T>泛型對象,經過跟蹤信息能夠看出,Expression<T>對象繼承自 LambdaExpression對象,而LambdaExpression對象又繼承自Expression抽象類,而在抽象裏重寫了ToString 方法,因此咱們在看到的時候是ToString以後的字符串表示形式。

Lambda表達式對象主要有兩部分 組成,從左向右依次是參數和邏輯主題,也就對應着Parameters和Body兩個公開屬性。在Parameters是全部參數的自讀列表,使用的是 System.Collection.ObjectModel.ReadOnlyCollection<T>泛型對象來存儲。

這裏也許你已經參數疑問,貌似表達式 目錄樹的構建真的很完美,每一個細節都有指定的對象來表示。不錯,在.NET3.5框架中引入了不少用來表示表達式樹邏輯節點的對象。這些對象都是直接或間 接的繼承自Expression抽象類,該類表示抽象的表達式節點。咱們都知道表達式節點各類各樣,須要具體化後才能直接使用。因此在基類 Expression中只有兩個屬性,一個是public ExpressionType NodeType { get; },表示當前表達式節點的類型,還有另一個public Type Type { get; },表示當前表達式的靜態類型。何爲靜態類型,就是說當沒有變成表達式目錄樹的時候是什麼類型,具體點講也就是委託類型。由於在委託類型被 Expression<T>泛型包裝後,編譯器是把它自動的編譯成表達式樹的數據結構類型,因此這裏須要保存下當前節點的真實類型以備未來使 用。

小結:到了這裏其實已經把LINQ的一些準備工做講完了,從一系列的語法加強到.NET5.0的類庫的添加,已經爲後面的LINQ的到來鋪好了道路。下面的幾個小結將是最精彩的時刻,請不要錯過哦。

原文出處:http://www.cnblogs.com/wangiqngpei557/archive/2012/11/10/2764201.html

相關文章
相關標籤/搜索