Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类
众所周知,如果一个类可以被枚举,那么这个类必须要实现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
.locals init ([] class [mscorlib]System.Collections.Generic.List`<class [mscorlib]System.Action> list,
[] valuetype [mscorlib]System.Collections.Generic.List`/Enumerator<class [mscorlib]System.Action> V_1,
[] class [mscorlib]System.Action item)
IL_0000: nop
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`<class [mscorlib]System.Action>::.ctor()
IL_0006: stloc.
IL_0007: nop
IL_0008: ldloc.
IL_0009: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`/Enumerator<!> class [mscorlib]System.Collections.Generic.List`<class [mscorlib]System.Action>::GetEnumerator()
IL_000e: stloc.
.try
{
IL_000f: br.s IL_0021
IL_0011: ldloca.s V_1
IL_0013: call instance ! valuetype [mscorlib]System.Collections.Generic.List`/Enumerator<class [mscorlib]System.Action>::get_Current()
IL_0018: stloc.
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`/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`/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类型。。。

[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]
get
{
return this.current;
}
}
[__DynamicallyInvokable]
object IEnumerator.Current
{
[__DynamicallyInvokable]
get
{
if (this.index == || this.index == this.list._size + )
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
}
return this.Current;
}
}
internal Enumerator(List<T> list)
{
this.list = list;
this.index = ;
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 + ;
this.current = default(T);
return false;
}
[__DynamicallyInvokable]
void IEnumerator.Reset()
{
if (this.version != this.list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
this.index = ;
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看一下。

仔细查看上面的代码,原来所谓的yield会给你生成一个枚举类,而这个枚举类和刚才List中的Enumerator枚举类又无比的一样,如果你理解了显示
的枚举类Enumerator,我想这个匿名的枚举类Enumerator应该就非常简单了。
好了,大概就说这么多了,有了这个基础,我相信linq中返回的那些匿名枚举类对你来说应该就没什么问题了~~~
Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类的更多相关文章
- Linq专题之提高编码效率—— 第二篇 神一样的匿名类型
说起匿名类型,我们都知道这玩意都是为linq而生,而且匿名类型给我们带来的便利性大家在实战中应该都体会到了,特别适合于一次性使用,临时 使用这些场景,虽然说是匿名类型,也就是说是有类型的,只是匿名了而 ...
- Linq专题之提高编码效率—— 第一篇 Aggregate方法
我们知道linq是一个很古老的东西,大家也知道,自从用了linq,我们的foreach少了很多,但有一个现实就是我们在实际应用中使用到的却是屈指可数 的几个方法,这个系列我会带领大家看遍linq,好的 ...
- Kotlin——中级篇(五):枚举类(Enum)、接口类(Interface)详解
在上一章节中,详细的类(class)做了一个实例讲解,提到了类(class)的实例化.构造函数.声明.实现方式.和Java中类的区别等.但是对于Kotlin中的类的使用还远远不止那些.并且在上文中提到 ...
- Python笔记_第三篇_面向对象_5.一个关于类的实例(人开枪射击子弹)
1. 我们学了类的这些东西,用这些类我们来操作一个关于类的实例. 2. 题目:人开枪射击子弹,然后具有装弹动作,然后再开枪. 第一步:设计类: 人类名:Person属性:gun行为:fire,fill ...
- 程序猿(媛)的葵花宝典-- 必备idea 插件plugins 提高编码效率
最近发现了几个非常好用 提高编码效率 的idea 插件 跟大家分享一下,,,不用谢我!!!!!!!!!!!!! 因为idea自带的插件下载可能连接不上服务器而导致插件下载失败,所以这里推荐使用引入 ...
- 必备idea 插件plugins 提高编码效率
最近发现了几个非常好用 提高编码效率 的idea 插件 跟大家分享一下 因为idea自带的插件下载可能连接不上服务器而导致插件下载失败,所以这里推荐使用引入外部插件的方式 插件包也给你们准备好了( ...
- 变通实现微服务的per request以提高IO效率(三)
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- 第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装
微信服务器和第三方服务器之间究竟是通过什么方式进行对话的? 下面,我们先看下图: 其实我们可以简单的理解: (1)首先,用户向微信服务器发送消息: (2)微信服务器接收到用户的消息处理之后,通过开发者 ...
- PHP 性能分析第三篇: 性能调优实战
注意:本文是我们的 PHP 性能分析系列的第三篇,点此阅读 PHP 性能分析第一篇: XHProf & XHGui 介绍 ,或 PHP 性能分析第二篇: 深入研究 XHGui. 在本系列的 ...
随机推荐
- jQuery-1.9.1源码分析系列(二)jQuery选择器续2——筛选
前面分析了选择器的结构和几个解析函数,接下来分析jQuery对象的伪类选择器.这里所谓的jQuery对象的伪类选择器就是从已有的jQuery对象(元素集合)中筛选出指定的集合出来. 4. jQu ...
- javascript设计模式实践之迭代器--具有百叶窗切换图片效果的JQuery插件(一)
类似于幻灯片的切换效果,有时需要在网页中完成一些图片的自动切换效果,比如广告,宣传,产品介绍之类的,那么单纯的切就没意思了,需要在切换的时候通过一些效果使得切换生动些. 比较常用之一的就是窗帘切换了. ...
- 多个提高C#编程能力的建议
1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 readonly 3.在 as 和 强制类型转换之间,优先使用 as 操作符 4 ...
- jQuery手机菜单
效果展示 http://hovertree.com/texiao/nav/4/ 手机扫描二维码查看效果: 源码下载 http://hovertree.com/h/bjaf/kroft6c7.htm ...
- Mybatis框架中实现双向一对多关系映射
学习过Hibernate框架的伙伴们很容易就能简单的配置各种映射关系(Hibernate框架的映射关系在我的blogs中也有详细的讲解),但是在Mybatis框架中我们又如何去实现 一对多的关系映射呢 ...
- 【NopCommerce源码架构学习-一】--初识高性能的开源商城系统cms
很多人都说通过阅读.学习大神们高质量的代码是提高自己技术能力最快的方式之一.我觉得通过阅读NopCommerce的源码,可以从中学习很多企业系统.软件开发的规范和一些新的技术.技巧,可以快速地提高我们 ...
- Ehcache 缓存使用
在开发高并发量,高性能的网站应用系统时,缓存Cache起到了非常重要的作用.本文主要介绍EHCache的使用,以及使用EHCache的实践经验. 笔者使用过多种基于Java的开源Cache组件,其中包 ...
- C++_系列自学课程_第_4_课_string_《C++ Primer 第四版》
相信学习过计算机编程的人,基本应该都接触过C语言,在C语言中处理字符串大家一定多遇到过, 也都知道处理字符串非常麻烦,而在C++里面,由标准库string类提供对可变长的字符串的支持.下面 来看看st ...
- eclipse控制台下实现jdbc简单的增删改查测试
1.现在MySQL中创建一个表 2.首先创建一个类 //导入的包 import java.sql.Connection;import java.sql.DriverManager;import jav ...
- struts2中错误There is no Action mapped for namespace [/] and action name [] associated with context path
1 There is no Action mapped for namespace [/] and action name [] associated with context path [/Stru ...