從IL認識關鍵字(五)


關鍵字

   上一篇研究了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也簡單

  1. 判斷委托(CS$<>9__CachedAnonymousMethodDelegate1)是否為空,若為空創建一個<Main>b_0函數的委托
  2. 調用System.Linq.Enumerable.Where<int>(CS$<>9__CachedAnonymousMethodDelegate1)函數
  3. 將結果賦值局部變量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

View Code
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);
}

總結

   終於算寫完這個系列,最后一篇拖了很久,因為中間有事忙,還小病了一場。第一次寫一個系列,雖然不是什么高深的技術,只是把自己平時沒注意,沒去深挖的的知識去走一遍。總的來說,收獲還是不少。還會堅持寫博客,算是記錄下自己學習的過程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM