博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类
阅读量:5776 次
发布时间:2019-06-18

本文共 5035 字,大约阅读时间需要 16 分钟。

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

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

 

一: 探索IEnumerable

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

 

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

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

 

C#代码:

static void Main(string[] args)        {            List
list = new List
(); 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
list, [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
V_1, [2] class [mscorlib]System.Action item) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1
::.ctor() IL_0006: stloc.0 IL_0007: nop IL_0008: ldloc.0 IL_0009: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator
class [mscorlib]System.Collections.Generic.List`1
::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
::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
::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
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
, IDisposable, IEnumerator 3 { 4 private List
list; 5 private int index; 6 private int version; 7 private T current; 8 [__DynamicallyInvokable] 9 public T Current 10 { 11 [__DynamicallyInvokable] 12 get 13 { 14 return this.current; 15 } 16 } 17 [__DynamicallyInvokable] 18 object IEnumerator.Current 19 { 20 [__DynamicallyInvokable] 21 get 22 { 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
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
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 Person 14 { 15 public static IEnumerable
Run() 16 { 17 List
list = new List
(); 18 19 foreach (var item in list) 20 { 21 yield return item; 22 } 23 } 24 }

 

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

 

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

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

 

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

 

转载于:https://www.cnblogs.com/zhuxinxin/p/5276254.html

你可能感兴趣的文章
实时编辑
查看>>
KVO原理分析及使用进阶
查看>>
【348天】每日项目总结系列086(2018.01.19)
查看>>
【294天】我爱刷题系列053(2017.11.26)
查看>>
Microsoft发布了Azure Bot Service和LUIS的GA版
查看>>
Google发布Puppeteer 1.0
查看>>
.NET开源现状
查看>>
可替换元素和非可替换元素
查看>>
2016/08/25 The Secret Assumption of Agile
查看>>
(Portal 开发读书笔记)Portlet间交互-PortletSession
查看>>
搭建vsftpd服务器,使用匿名账户登入
查看>>
AMD改善Linux驱动,支持动态电源管理
查看>>
JAVA中循环删除list中元素的方法总结
查看>>
Java虚拟机管理的内存运行时数据区域解释
查看>>
人人都会深度学习之Tensorflow基础快速入门
查看>>
ChPlayer播放器的使用
查看>>
js 经过修改改良的全浏览器支持的软键盘,随机排列
查看>>
Mysql读写分离
查看>>
Oracle 备份与恢复学习笔记(5_1)
查看>>
Oracle 备份与恢复学习笔记(14)
查看>>