从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也简单
- 判断委托(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); }
总结
终于算写完这个系列,最后一篇拖了很久,因为中间有事忙,还小病了一场。第一次写一个系列,虽然不是什么高深的技术,只是把自己平时没注意,没去深挖的的知识去走一遍。总的来说,收获还是不少。还会坚持写博客,算是记录下自己学习的过程。