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. 在本系列的 ...
随机推荐
- (转载)基于LBS地图的开发,满足地图上有头像的需求
最近做的项目主要是LBS这块 主打成员定位功能 我们的UI设计是这样的 乍一看上去是挺好挺美观的 不同的人会显示不同的头像 可是当人扎堆的时候 问题就来了 当人多的时候(例如上图所示) 地图滑动起来就 ...
- 1Z0-053 争议题目解析304
1Z0-053 争议题目解析304 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 304.What privileges must be granted to allow an a ...
- CSS3魔法堂:说说Multi-column Layout
前言 是否记得<读者文摘>中那一篇篇优美感人的文章呢?那除了文章内容外,还记得那报刊.杂志独有的多栏布局吗? 当我们希望将报刊.杂志中的阅读体验迁移到网页上时,最简单直接的方式就是采用 ...
- Winform混合式开发框架访问Web API接口的处理
在我的混合式开发框架里面,集成了WebAPI的访问,这种访问方式不仅可以实现简便的数据交换,而且可以在多种平台上进行接入,如Winform程序.Web网站.移动端APP等多种接入方式,Web API的 ...
- .NET正则表达式基础入门(一)
字符组 正则表达式的字符组十分简单,就是列出你所想要匹配的字符.阅读本章前,建议先下载我于CSDN上传的示例代码,下载无需分数,下载链接. 1.普通字符组 普通字符组,列出所有你需要匹配的字符. 例如 ...
- 异构(兼容dubbo)SOA系统架构(.net)优化升级
前面一片文章已经提到我司的异构(兼容dubbo)SOA系统架构,解决了不少技术痛点,也还算比较完善,也顺利推广开来. 但作为项目的开发者,自己产品的问题心里是清楚的,离自己满意还是有不小的距离. 在推 ...
- c# asp.net4.0尚未在web服务器上注册
辗转无数论坛,感谢http://bbs.csdn.net/topics/390992746 microsoft官方文档: https://blogs.msdn.microsoft.com/webdev ...
- python基础之数据类型(二)
Python3 元组 Python 的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 不可变的tupl ...
- Lind.DDD.Events事件总线~自动化注册
回到目录 让大叔兴奋的自动化注册 对于领域事件之前说过,在程序启动时订阅(注册)一些事件处理程序,然后在程序的具体位置去发布(触发)它,这是传统的pub/sub模式的体现,当然也没有什么问题,为了让它 ...
- JMeter专题系列(七)聚合报告之 90% Line
JMeter 官网原文: 90% Line - 90% of the samples took no more than this time. The remaining samples at lea ...