關鍵字
上一篇研究了lock關鍵字,最后一篇討論一下Linq的關鍵字,對於Linq存在的爭議院子里老趙的一篇文章已經解釋過,這里以Linq To Objects來看Linq關鍵字。Linq定義了很多關鍵字,這里只列舉其中的where orderby groupby select關鍵字,其他關鍵字用到也是相同道理。
MSDN解釋
語言集成查詢 ( Language INtegrated Query ) 是一組技術的名稱,這些技術建立在將查詢功能直接集成到 C# 語言(以及 Visual Basic 和可能的任何其他 .NET 語言)的基礎上。借助於 LINQ,查詢現在已是高級語言構造,就如同類、方法、事件等等。
從MSDN上看,Linq究竟是什么,我是沒看明白。還是老老實實寫代碼反編譯看看。
Code And IL
下面定義一個數組,先從簡單的where條件開始看
private static int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; static void Main(string[] args) { var result = from num in nums where num > 5 select num; Console.Read(); }
利用Reflector反編譯上面代碼,得出下面IL:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 4 .locals init ( [0] class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> enumerable) L_0000: nop L_0001: ldsfld int32[] Test.Program::nums L_0006: ldsfld class [mscorlib]System.Func`2<int32, bool> Test.Program::CS$<>9__CachedAnonymousMethodDelegate1 L_000b: brtrue.s L_0020 L_000d: ldnull L_000e: ldftn bool Test.Program::<Main>b__0(int32) L_0014: newobj instance void [mscorlib]System.Func`2<int32, bool>::.ctor(object, native int) L_0019: stsfld class [mscorlib]System.Func`2<int32, bool> Test.Program::CS$<>9__CachedAnonymousMethodDelegate1 L_001e: br.s L_0020 L_0020: ldsfld class [mscorlib]System.Func`2<int32, bool> Test.Program::CS$<>9__CachedAnonymousMethodDelegate1 L_0025: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>) L_002a: stloc.0 L_002b: call int32 [mscorlib]System.Console::Read() L_0030: pop L_0031: ret }
上面標紅的的兩個對象(一個委托,一個函數),這是編譯器自己生成的,這段IL也簡單
- 判斷委托(CS$<>9__CachedAnonymousMethodDelegate1)是否為空,若為空創建一個<Main>b_0函數的委托
- 調用System.Linq.Enumerable.Where<int>(CS$<>9__CachedAnonymousMethodDelegate1)函數
- 將結果賦值局部變量enumerable
翻譯成C# Code如下面,由於編譯器的生成的名稱太長,也不能通過編譯,所以將名稱改了一下CS$<>9__CachedAnonymousMethodDelegate1 => _delegate <Main>b_0 => b_0
private static Func<int,bool> _delegate = null; static void Code() { if (_delegate == null) { _delegate = new Func<int,bool>(b_0); } IEnumerable<int> enumerable = System.Linq.Enumerable.Where(nums,_delegate); } static bool b_0(int n) { return n > 5; }
將兩段代碼放在一起,便能看出其中的奧秘。
從上面代碼可以看出,Linq語法最終轉換成System.Linq.Enumerable.Where(nums,_delegate)的調用,where 條件后面帶返回值true,實際上where條件轉換委托,select決定IEnumerable<T>的返回值,實際上若select返回不是from 的 Item,將調用System.Linq.Enumerable.Select(TSource,TResult)返回相應對象。
根據這個例子猜測,實際上Linq的語法最終通過編譯器,生成需要的對象,調用System.Linq.Enumerable里面的的擴展方法。當然這里說的都是Framework里面的集合,對象。我們也可以擴展自己的Linq。下面試一個復雜一點的Linq語句(where + orderby),我們猜想orderby應該也有相應的擴展函數,調用順序應該先調用where篩選數據,然后排序
復雜一點的Linq
private static int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; static void Main(string[] args) { var result = from num in nums where num > 5 orderby num.ToString().Substring(0) select num; Console.Read(); }
這里不把IL貼出來,大家可以自己反編譯看看,下面直接把將IL翻譯的Code
private static Func<int,bool> _delegate = null; private static Func<int,string> _delegate1 = null; static void Code() { if (_delegate == null) { _delegate = new Func<int,bool>(b_0); } if(_delegate1 == null) { _delegate1 = new Func<int,string>(b_1); } IEnumerable<int> enumerable = Enumerable.OrderBy(Enumerable.Where(nums,_delegate),_delegate1); } static bool b_0(int n) { return n > 5; } static string b_1(int n) { return n.ToString().Substring(0); }
這里可以看出,orderby又生成了一個對應的委托。orderby內部實現,通過調用數據源的Sort函數排序,這里就是Array.Sort排序,即快速排序。
自己的Linq

public class MyLinq<T> : IEnumerable<T>,IEnumerator<T> { T[] array = new T[10]; T current; int index = 0; #region IEnumerable<T> 成員 public IEnumerator<T> GetEnumerator() { return this; } #endregion #region IEnumerable 成員 IEnumerator IEnumerable.GetEnumerator() { return this; } #endregion #region IEnumerator<T> 成員 public T Current { get { return current; } } #endregion #region IDisposable 成員 public void Dispose() { } #endregion #region IEnumerator 成員 object IEnumerator.Current { get { return current; } } public bool MoveNext() { if(index < array.Length) { this.current = array[index++]; return true; } return false; } public void Reset() { this.index = 0; } #endregion }
調用代碼
MyLinq<int> ml = new MyLinq<int>(); var result = from m in ml select m; foreach(var item in result) { Console.WriteLine(item); }
總結
終於算寫完這個系列,最后一篇拖了很久,因為中間有事忙,還小病了一場。第一次寫一個系列,雖然不是什么高深的技術,只是把自己平時沒注意,沒去深挖的的知識去走一遍。總的來說,收獲還是不少。還會堅持寫博客,算是記錄下自己學習的過程。