Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类

来源:互联网 发布:大学java 教不教线程 编辑:程序博客网 时间:2024/05/18 05:25
 

   众所周知,如果一个类可以被枚举,那么这个类必须要实现IEnumerable接口,而恰恰我们所有的linq都是一个继承自IEnumerable接口的匿名类,

那么问题就来了,IEnumerable使了何等神通让这些集合类型可以被自由的枚举???

 

一: 探索IEnumerable

  首先我们看看此接口都定义了些什么东西,如ILSpy所示:

 

从这个接口中,好像也仅仅有一个IEnumerator接口类型的方法之外,并没有可以挖掘的东西,这时候大家就应该好奇了,foreach既然可以枚举Collection,

那foreach背后的机制和GetEnumerator()有什么关系呢???说干就干,我们写一个demo,用ILDasm看看背后的IL应该就清楚了。

 

C#代码:

     static void Main(string[] args)        {            List<Action> list = new List<Action>();            foreach (var item in list)            {                Console.WriteLine();            }        }

 

IL代码:

.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // Code size       60 (0x3c)  .maxstack  1  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action> list,           [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action> V_1,           [2] class [mscorlib]System.Action item)  IL_0000:  nop  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action>::.ctor()  IL_0006:  stloc.0  IL_0007:  nop  IL_0008:  ldloc.0  IL_0009:  callvirt   instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action>::GetEnumerator()  IL_000e:  stloc.1  .try  {    IL_000f:  br.s       IL_0021    IL_0011:  ldloca.s   V_1    IL_0013:  call       instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>::get_Current()    IL_0018:  stloc.2    IL_0019:  nop    IL_001a:  call       void [mscorlib]System.Console::WriteLine()    IL_001f:  nop    IL_0020:  nop    IL_0021:  ldloca.s   V_1    IL_0023:  call       instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>::MoveNext()    IL_0028:  brtrue.s   IL_0011    IL_002a:  leave.s    IL_003b  }  // end .try  finally  {    IL_002c:  ldloca.s   V_1    IL_002e:  constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>    IL_0034:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()    IL_0039:  nop    IL_003a:  endfinally  }  // end handler  IL_003b:  ret} // end of method Program::Main

 

从IL中标红的字体来看,原来所谓的foreach,本质上调用的是list的GetEnumerator()方法来返回一个Enumerator枚举类型,然后在while循环中通过

current获取当前值,然后用MoveNext()获取下一个值,以此类推,如果把IL还原一下,大概就是下面这样:

            var enumerator = list.GetEnumerator();            try            {                while (enumerator.MoveNext())                {                    Console.WriteLine(enumerator.Current);                }            }            finally            {                enumerator.Dispose();            }

 

这个时候你是不是有种强烈的欲望来探索GetEnumerator()到底干了什么,以及MoveNext()在其中扮演了什么角色??? 下面我们用ILSpy看看List下面

所谓的Enumerator类型。。。

 

 1     [Serializable] 2         public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator 3         { 4             private List<T> list; 5             private int index; 6             private int version; 7             private T current; 8             [__DynamicallyInvokable] 9             public T Current10             {11                 [__DynamicallyInvokable]12                 get13                 {14                     return this.current;15                 }16             }17             [__DynamicallyInvokable]18             object IEnumerator.Current19             {20                 [__DynamicallyInvokable]21                 get22                 {23                     if (this.index == 0 || this.index == this.list._size + 1)24                     {25                         ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);26                     }27                     return this.Current;28                 }29             }30             internal Enumerator(List<T> list)31             {32                 this.list = list;33                 this.index = 0;34                 this.version = list._version;35                 this.current = default(T);36             }37             [__DynamicallyInvokable]38             public void Dispose()39             {40             }41             [__DynamicallyInvokable]42             public bool MoveNext()43             {44                 List<T> list = this.list;45                 if (this.version == list._version && this.index < list._size)46                 {47                     this.current = list._items[this.index];48                     this.index++;49                     return true;50                 }51                 return this.MoveNextRare();52             }53             private bool MoveNextRare()54             {55                 if (this.version != this.list._version)56                 {57                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);58                 }59                 this.index = this.list._size + 1;60                 this.current = default(T);61                 return false;62             }63             [__DynamicallyInvokable]64             void IEnumerator.Reset()65             {66                 if (this.version != this.list._version)67                 {68                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);69                 }70                 this.index = 0;71                 this.current = default(T);72             }73         }

 

通过查看所谓的Enumerator类的定义,尤其是标红的地方,可能会让你顿然醒悟,其实所谓的枚举类,仅仅是一个枚举集合的包装类,比如这里的List,

然后枚举类通过index++ 这种手段来逐一获取List中的元素,仅此而已。

 

二:yield关键词

  当大家明白了所谓的枚举类之后,是不是想到了一个怪异的yield词法,这个掉毛竟然还可以被枚举,就比如下面这样代码:

 1 class Program 2 { 3     static void Main(string[] args) 4     { 5         foreach (var item in Person.Run()) 6         { 7             Console.WriteLine(item); 8         } 9 10     }11 }12 13 class Person14 {15     public static IEnumerable<int> Run()16     {17         List<int> list = new List<int>();18 19         foreach (var item in list)20         {21             yield return item;22         }23     }24 }

 

那究竟yield干了什么呢? 而且能够让它人可以一探究竟??? 我们用ILDasm看一下。

 

仔细查看上面的代码,原来所谓的yield会给你生成一个枚举类,而这个枚举类和刚才List中的Enumerator枚举类又无比的一样,如果你理解了显示

的枚举类Enumerator,我想这个匿名的枚举类Enumerator应该就非常简单了。

 

好了,大概就说这么多了,有了这个基础,我相信linq中返回的那些匿名枚举类对你来说应该就没什么问题了~~~

 

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 电脑账号删除后电脑开不了怎么办 lol不小心融错了皮肤怎么办 农村医保交费了没有录入系统怎么办 电动三轮车在泗阳被交警扣了怎么办 环评证实际的设备少报了怎么办 别人盗用了自己的社保卡怎么办 如果有人盗用社保卡信息住院怎么办 丈夫出轨生下孩子妻子该怎么怎么办 l老公偷着儿子消失了怎么办 老婆出轨怀孕现在流了他跑了怎么办 交通银行贷款资金户里面的钱怎么办 新车2个月出现好多小毛病怎么办 地上终末之日尸潮破坏房子怎么办 初一数学期未考试考了77分怎么办 生育服务单再婚婚史情况怎么办 我孩子的数学一直很差怎么办呀! 扶桑花的叶子出斑点发黄怎么办 高二的学生成绩下降特别大怎么办 六个月大的宝宝咳嗽伴有气喘怎么办 昨天奶用力吸今天好疼怎么办 律师把医院的医药费单子丢了怎么办 母亲是个不明事理的人怎么办 华西医院的就诊卡掉了怎么办 整形医院把我脸上疤痕被大了怎么办 牙齿缝里的东西很臭怎么办 五岁宝宝牙齿有空洞斑点怎么办 给别人担保贷款被起诉了怎么办 不小心在微信里登录江西移动怎么办 住院出院后医保卡在医院怎么办 医院门诊做完检查医生下班了怎么办 退税的发票勾选为抵扣的发票怎么办 买完邮轮票后护照换了怎么办? 朝阳医院挂号过了取号时间怎么办 苹果4的id密码忘了怎么办 ipad己停用5分钟后再试怎么办 手机上大智慧日线失真怎么办 安卓手机突然地图信号弱怎么办 魔兽争霸对战模式没有金币要怎么办 常州号码被标记了商铺的名字怎么办 车提档了不接收怎么办公司能收回吗 稳岗补贴如果联系方式填错了怎么办