程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Linq系列:基礎與本質(Part I)

Linq系列:基礎與本質(Part I)

編輯:關於.NET

之前寫過一些C#3.x新的特性。請參考:C#3.x特性,我們知道這些新的特性基本都是為實現LINQ服務的,在平常的編程中也可以有選擇的合 理應用,也會有效提高編碼效率,實現可讀性比較強的簡潔代碼。在認識這些特性的基礎上,理解認識LINQ將變得簡單了。

1 LINQ簡介:

LINQ 查詢表達式(query expressions )可以使用統一的方式對實現IEnumberable<T>接口的對象、關系數據庫、數據集(Datasets )以及XML文檔進行訪問。

嚴格的說,LINQ是用來描述一系列數據訪問技術的術語。LINQ to Objects 適用於實現IEnumberable<T>接口的對象,LINQ to SQL 適用於關系數據庫, LINQ to DataSet is則是 LINQ to SQL的一個子集, LINQ to XML 適用於XML 文檔。LINQ 查詢表達式 是強類型的 (strongly typed),因此編譯器會確保其語法正確性。LINQ是一個可擴展技術,第三方可以使用擴展函數來定義新的查詢操作符。

2LINQ核心程序集(Assembly)

至少需要引用System.Linq 命名空間,在System.Core.dll中定義,Visual stuodio 2008默認會自動添加引用。

System.Core.dll Defines the types that represent the core LINQ API. This is the one assembly you must have access to. System.Data.Linq.dll Provides functionality for using LINQ with relational databases (LINQ to SQL). System.Data.DataSetExtensions.dll Defines a handful of types to integrate ADO.NET types into the LINQ programming paradigm (LINQ to DataSet). System.Xml.Linq.dll Provides functionality for using LINQ with XML document data (LINQ to XML).

3 LINQ例子

Code

1 static void QueryOverInts()
2     {
3       int[] numbers = { 10, 20, 28, 40, 1, 3, 5, 8 };
4       IEnumerable<int> subset = from i in numbers
5                    where i < 10
6                    select i;
7       IEnumerable<int> subset1 = numbers.Where( j => j < 10 ).
8                     OrderBy( j => j );
9       foreach( int i in subset )
10         Console.WriteLine( "Item: {0}", i );
11       foreach( int i in subset1 )
12         Console.WriteLine( "Item: {0}", i );
13
14

我們看上面的代碼定義了兩個集合subset和subset1。分別通過查詢表達式和Lambda表達式生成。那麼LINQ內部到底是怎麼實現的?這兩種 方式到底有什麼不同呢。我們先來看看這段代碼長生的IL。

Code

1 IL_0014: ldsfld   class [System.Core]System.Func`2<int32,bool> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
2  IL_0019: brtrue.s  IL_002e
3  IL_001b: ldnull
4  IL_001c: ldftn   bool LINQProject.Program::'<QueryOverInts>b__0'(int32)
5  IL_0022: newobj   instance void class [System.Core]System.Func`2<int32,bool>::.ctor(object,
6                                             native int)
7  IL_0027: stsfld   class [System.Core]System.Func`2<int32,bool> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
8  IL_002c: br.s    IL_002e
9  IL_002e: ldsfld   class [System.Core]System.Func`2<int32,bool> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
10  IL_0033: call    class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core] System.Linq.Enumerable::Where<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>,
11                           class [System.Core]System.Func`2<!!0,bool>)
12  IL_0038: stloc.1
13  IL_0039: ldloc.0
14  IL_003a: ldsfld   class [System.Core]System.Func`2<int32,bool> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
15  IL_003f: brtrue.s  IL_0054
16  IL_0041: ldnull
17  IL_0042: ldftn   bool LINQProject.Program::'<QueryOverInts>b__1'(int32)
18  IL_0048: newobj   instance void class [System.Core]System.Func`2<int32,bool>::.ctor(object,
19                                             native int)
20  IL_004d: stsfld   class [System.Core]System.Func`2<int32,bool> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
21  IL_0052: br.s    IL_0054
22  IL_0054: ldsfld   class [System.Core]System.Func`2<int32,bool> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
23  IL_0059: call    class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core] System.Linq.Enumerable::Where<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>,
24                         class [System.Core]System.Func`2<!!0,bool>)
25  IL_005e: ldsfld   class [System.Core]System.Func`2<int32,int32> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate5'
26  IL_0063: brtrue.s  IL_0078
27  IL_0065: ldnull
28  IL_0066: ldftn   int32 LINQProject.Program::'<QueryOverInts>b__2'(int32)
29  IL_006c: newobj   instance void class [System.Core]System.Func`2<int32,int32>::.ctor(object,                                            native int)
30  IL_0071: stsfld   class [System.Core]System.Func`2<int32,int32> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate5'
31  IL_0076: br.s    IL_0078
32  IL_0078: ldsfld   class [System.Core]System.Func`2<int32,int32> LINQProject.Program::'CS$<>9__CachedAnonymousMethodDelegate5'
33  IL_007d: call    class [System.Core]System.Linq.IOrderedEnumerable`1<!!0> [System.Core] System.Linq.Enumerable::OrderBy<int32,int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>,class [System.Core]System.Func`2<!!0,!!1>)

我們看看IL代碼的1-11行是subset的長生過程,14-33行是subset1的長生過程。我們先不看25-33行的subset1代碼,先關注1-11行,14 -24行的代碼。我們發現這裡的代碼除了使用不同的委托外,實際是一樣的。先不對其講解,我們就能確定,通過查詢表達式和Lambda表達式 生成兩個集合subset和subset1對於CLR來說是一樣的,沒有什麼區別。正如我之前在C#3.X系列提到的這些特性是基於編譯器的新特性,在CLR 層並沒有提供新的實質內容,這裡LINQ也是一樣。編譯器會最終實現一個語法映射的過程,將查詢表達式翻譯,映射成Lambda表達式的形式。

我們了解了其大觀上的實現原理,那麼我們就仔細看看其具體實現過程。請看IL代碼的1-11行。這裡是對where語法的實現,我們很容易的 看到這裡用到的一個委托。這個委托是編譯器自動生成的一個靜態委托量CS$<>9__CachedAnonymousMethodDelegate3。而這個變量正是 來自於System.Func這個新類。這裡我們就可以知道LINQ實質還是需要調用委托。除了委托,我們還可以看到編譯器會生成一個靜態方法: <QueryOverInts>b__0。在這個方法裡對你的查詢表達式的查詢條件進行處理。LINQ實現的關鍵就是代碼10-11行。這裡我們看到系統會 調用System.Linq.Enumerable.Where<T>方法,結果集也正是通過此方法得到的,傳入的第二個參數是由編譯器生成的匿名方法,也就是 上面說的委托變量。看到這裡大家應該對LINQ的工作本質有個大概的了解。至於代碼的25-33行,是用 System.Linq.Enumerable::OrderBy<int32,int32>方法去實現查詢的Orderby語法。實現原理同以上對where的講解。

我們對以上subset和subset1調用一下代碼:

Code

1 static void ReflectOverQueryResults( object resultSet )
2     {
3       Console.WriteLine( "resultSet is of type: {0}", resultSet.GetType().Name );
4       Console.WriteLine( "resultSet location: {0}", resultSet.GetType().Assembly );
5

我們可以得出subset的類型是<WhereIterator>d__0`1,subset1的類型是OrderedEnumerable`2,可見LINQ表達式的形式不同,其結 果類型就不同。但是由於以上Where和OrderBy都實現了IEnumerable<T>,所以可以寫成上面的代碼形式。根據以上分析,在獲取LINQ的 返回結果時,最好使用 var 關鍵字。如:

var subset = from i in numbers where i < 10 select i;

4 LINQ特性

我們對上面的實例代碼加上下面幾行:

Code

numbers[ 0 ] = 5;
foreach( int i in subset )
 Console.WriteLine( "Item: {0}", i );

我們可以看到subset結果集前後輸出的不同點是一個為10,一個為5,其余元素一樣。這裡我們就可以看到LINQ 查詢表達式只有在迭代訪問 其內容時,才會被計算並執行。這樣可以保證每次訪問得到的是最新的數據。

以上是查詢的延時執行,那麼查詢的立即執行怎麼實現呢?和簡單,我們只需要在查詢表達試ToList<T>()就可以了。將查詢結果直 接放到強類型的結果集中,執行後,這些結果集就和查詢表達式沒有關系了,可以單獨操作。如:IEnumerable<int> subset3 = ( from i in numbers where i < 10 select i).ToList<int>();

這裡我們經常會提到System.Linq.Enumerable和System.Func。接下來將會分析這些。

待續。。。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved