Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类
众所周知,如果一个类可以被枚举,那么这个类必须要实现IEnumerable接口,而恰恰我们所有的linq都是一个继承自IEnumerable接口的匿名类,
那么问题就来了,IEnumerable使了何等神通让这些集合类型可以被自由的枚举???
一: 探索IEnumerable
首先我们看看此接口都定义了些什么东西,如ILSpy所示:
![](http://images2015.cnblogs.com/blog/214741/201603/214741-20160314134103053-932815533.png)
从这个接口中,好像也仅仅有一个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(); while (enumerator.MoveNext()) Console.WriteLine(enumerator.Current); finally enumerator.Dispose(); }
这个时候你是不是有种强烈的欲望来探索GetEnumerator()到底干了什么,以及MoveNext()在其中扮演了什么角色??? 下面我们用ILSpy看看List下面
所谓的Enumerator类型。。。
![](http://images2015.cnblogs.com/blog/214741/201603/214741-20160314140303318-251932947.png)
[Serializable] public struct Enumerator : IEnumerator T , IDisposable, IEnumerator private List T list; private int index; private int version; private T current; [__DynamicallyInvokable] public T Current [__DynamicallyInvokable] return this.current; [__DynamicallyInvokable] object IEnumerator.Current [__DynamicallyInvokable] if (this.index == 0 || this.index == this.list._size + 1) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); return this.Current; internal Enumerator(List T list) this.list = list; this.index = 0; this.version = list._version; this.current = default(T); [__DynamicallyInvokable] public void Dispose() [__DynamicallyInvokable] public bool MoveNext() List T list = this.list; if (this.version == list._version this.index list._size) this.current = list._items[this.index]; this.index++; return true; return this.MoveNextRare(); private bool MoveNextRare() if (this.version != this.list._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); this.index = this.list._size + 1; this.current = default(T); return false; [__DynamicallyInvokable] void IEnumerator.Reset() if (this.version != this.list._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); this.index = 0; this.current = default(T); }
通过查看所谓的Enumerator类的定义,尤其是标红的地方,可能会让你顿然醒悟,其实所谓的枚举类,仅仅是一个枚举集合的包装类,比如这里的List,
然后枚举类通过index++ 这种手段来逐一获取List中的元素,仅此而已。
二:yield关键词
当大家明白了所谓的枚举类之后,是不是想到了一个怪异的yield词法,这个掉毛竟然还可以被枚举,就比如下面这样代码:
class Program static void Main(string[] args) foreach (var item in Person.Run()) Console.WriteLine(item); class Person public static IEnumerable int Run() List int list = new List int foreach (var item in list) yield return item; }
那究竟yield干了什么呢? 而且能够让它人可以一探究竟??? 我们用ILDasm看一下。
![](http://images2015.cnblogs.com/blog/214741/201603/214741-20160314142012896-409766217.png)
仔细查看上面的代码,原来所谓的yield会给你生成一个枚举类,而这个枚举类和刚才List中的Enumerator枚举类又无比的一样,如果你理解了显示
的枚举类Enumerator,我想这个匿名的枚举类Enumerator应该就非常简单了。
好了,大概就说这么多了,有了这个基础,我相信linq中返回的那些匿名枚举类对你来说应该就没什么问题了~~~
C#基础知识学习之 ☀️ | 反射(Reflection) 的含义和用法 C# 反射(Reflection) 反射是.NET中的重要机制,通过反射可以得到*.exe或*.dll等程序集内部的接口、类、方法、字段、属性、特性等信息,还可以动态创建出类型实例并执行其中的方法。 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
第五章 继承 类,超类和子类 super和this看似有相似的概念, 但实际没有可比性,this持有对象本身的引用, 可以赋值给另一个变量,super则不具备这样的功能, 它只是一个指示编译器调用超类方法的关键字 调用super()构造器的语句必须是子类构造器的第一条语句 Object: 所有类的超类 判断两个对象是否相等可以使用Objects.
相关文章
- pycharm默认编码_编码需要注意什么
- Java 版 Unicode 编码和字符串互转,支持混合内容解码
- UTF-8编码规则_库德巴码编码规则字符编码笔记:ASCII,Unicode和UTF-8
- 【编码规范】HTML编码风格指南
- 智能处理与编码算法产品化;媒体传输协议的演进与未来;音视频播放技术与消费体验升级
- Linux编码格式探索之旅(查看linux的编码格式)
- 字符编码MySQL配置中文字符编码实现双语互通(mysql设置中文)
- 深入探索Linux视频编码源码(linux视频源码)
- 如何在Linux中查看文件编码格式(linux查看文件编码格式)
- Oracle 查询编码:一个指导技巧(oracle查询编码)
- Oracle连接中文编码的问题及解决方法(oracle连接编码)
- 如何查询MySQL数据库的编码格式?(查询mysql的编码格式)
- MySQL编码格式优化,提升数据处理效率(c mysql 编码格式)
- MySQL创建表编码设置(mysql中创建表的编码)
- 提升编码效率Oracle代码提示工具(oracle 代码提示)
- Oracle如何正确使用UTF8编码(oracle中utf8)
- Oracle中URL编码的技巧和解决方案(Oracle中url编码)
- MsSQL数据导入到Mongo的默认编码问题(正确导入Mongo的方法)