IEnumerable

饮水思源

《C#本质论》

Overview

根据定义,.Net 的中集合,本质上是一个类,它最起码实现了IEnumeraable 或者非泛型的IEnumerable 结构。 这个接口非常关键,如果想要支持对集合执行的遍历操作,子起码的要求就是实现由IEnumerable 规定的方法。

foreach 和 数组

看下面的代码

string[] strArr = { "", "", "" };
foreach (var item in strArr)
{
Console.WriteLine(item);
}

上面的代码想必已经非常的熟悉了,我们来看一下上面的代码究竟是什么样的

 
string[] array = new string[]
{
"",
"",
""
};
string[] array2 = array;
for (int i = ; i < array2.Length; i++)
{
string value = array2[i];
Console.WriteLine(value);
}

在上面的代码中,奖foreach编译为了for循环,这就需要依赖于数组的Length 属性来确定for循环的次数了。

foreach 和 IEnumerable

因为数据的长度是固定的,并且可以使用索引操作符,这样可以通过for循环的形式来达到遍历数组的目的,但是对IEnumerable 接口的集合,可能就不太适用了,比如说: stack<T> Queue<T> 等等,是不支持按照索引获取元素的,所以要有一个更加普适的方式来达到遍历的目的,这种方式就是迭代器 (iterator) 模式 , 它只需要确定,集合的第一个元素,下一个元素,和最后一个元素就能达到遍历的目的。

System.Collections.Generic.IEnumerator 和 System.Collections.IEnumerator 接口,分别实现了,泛型和非泛型的迭代器模式,可以使用迭代器模式遍历集合,而不是通过索引操作符来遍历。

继承层次类图

其中,IEnumerator 中三个成员, 只读属性object Current 代表当前的元素,方法bool MoveNext() 将元素移动到下一个元素,同时检测是否枚举完成了集合中的每一个元素。 void Reset() 方法永远不要主动调用他 , 该方法会抛出一个 NotImplemented Exception 异常,IEnumerator 重载了Current属性,提供了泛型的实现。

使用迭代器遍历集合

static void Main(string[] args)
{
Stack<string> stack = new Stack<string>();
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push(""); IEnumerator<string> enumerator = stack.GetEnumerator();
//使用迭代器遍历集合
while (enumerator.MoveNext())
{
string value = enumerator.Current;
Console.WriteLine(value);
}
}

当然使用While循环遍历的太麻烦了

static void Main(string[] args)
{
Stack<string> stack = new Stack<string>();
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push("");
stack.Push(""); foreach (var item in stack)
{
Console.WriteLine(item);
}
}

foreach遍历反编译的IL代码

.try
{
IL_004b: br.s IL_005e
// loop start (head: IL_005e)
IL_004d: ldloca.s
IL_004f: call instance ! valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>::get_Current()//获取Current只读属性
IL_0054: stloc.
IL_0055: nop
IL_0056: ldloc.
IL_0057: call void [mscorlib]System.Console::WriteLine(string)//输出
IL_005c: nop
IL_005d: nop IL_005e: ldloca.s
IL_0060: call instance bool valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>::MoveNext()//调用MoveNext方法
IL_0065: brtrue.s IL_004d
// end loop IL_0067: leave.s IL_0078
} // end .try
//回收资源
finally
{
IL_0069: ldloca.s
IL_006b: constrained. valuetype [System]System.Collections.Generic.Stack`/Enumerator<string>
IL_0071: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0076: nop
IL_0077: endfinally
} // end handler
 

最终的IL代码还是会编译成类似While循环的方式,foreach循环只不是帮助我们减少了不必要的代码量。

状态共享

请思考这么一个问题,上述的代码,如果循环的时候,再次嵌套一个循环再次访问stack 集合,那么,MoveNext方法会不会乱套呢?

答案是不会的? 请注意,在上面的类图中我们的集合类是没有直接的继承自IEnumerator<T> 接口的,而是继承了IEnumerable 接口,这个接口仅仅提供了一个方法,那就是获取IEnumerator<T> 对象,MoveNext() 方法的状态,不是有集合类来维持的,而是由类外一个类来维持状态的,这个类通常是一个内部类,以方便访问集合的成员。

清理状态

由于由实现了IEnumerator<T> 接口的类来维护状态,有时候在遍历完集合或者引发的异常的时候有时候需要清理一下状态,为了这种考虑,IEnumerator 集合还实现了,

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

 
 
 
 

IEnumerable<T>的更多相关文章

  1. 先说IEnumerable,我们每天用的foreach你真的懂它吗?

    我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...

  2. 为IEnumerable<T>添加RemoveAll<IEnumerable<T>>扩展方法--高性能篇

    最近写代码,遇到一个问题,微软基于List<T>自带的方法是public bool Remove(T item);,可是有时候我们可能会用到诸如RemoveAll<IEnumerab ...

  3. C# 索引器,实现IEnumerable接口的GetEnumerator()方法

    当自定义类需要实现索引时,可以在类中实现索引器. 用Table作为例子,Table由多个Row组成,Row由多个Cell组成, 我们需要实现自定义的table[0],row[0] 索引器定义格式为 [ ...

  4. Entity Framework中使用IEnumerable<T>、IQueryable<T>及IList<T>的区别

    1. IEnumerable<T> IEnumerable<T> :对于在内存中集合上运行的方法,返回的可枚举对象将捕获传递到方法的参数.在枚举该对象时,将使用查询运算符的逻辑 ...

  5. 你可能不知道的陷阱, IEnumerable接口

    1.  IEnumerable 与  IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...

  6. 通过IEnumerable和IDisposable实现可暂停和取消的任务队列

    一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...

  7. 判断IEnumerable<T>集合中是否包含有T对象

    比如,有角色集合中,只有用户创建有角色,才出现“分配”铵钮.反之,隐藏. IEnumerable有一个方法,叫Any:

  8. .NET面试题系列[11] - IEnumerable<T>的派生类

    “你每次都选择合适的数据结构了吗?” - Jeffery Zhao .NET面试题系列目录 ICollection<T>继承IEnumerable<T>.在其基础上,增加了Ad ...

  9. .NET面试题系列[10] - IEnumerable的派生类

    .NET面试题系列目录 IEnumerable分为两个版本:泛型的和非泛型的.IEnumerable只有一个方法GetEnumerator.如果你只需要数据而不打算修改它,不打算为集合插入或删除任何成 ...

  10. .NET面试题系列[9] - IEnumerable

    .NET面试题系列目录 什么是IEnumerable? IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumera ...

随机推荐

  1. 1.Unix,Linux起源与编译原理

    一.UNIX操作系统   作者:丹尼斯.里奇,肯.汤普逊  版权:贝尔实验室  时间:1971  特点:多用户,多任务(多进程),多CPU(多种CPU架构),高安全,高可靠,高性能,高稳定  应用:构 ...

  2. Zabbix应用六:Zabbix监控Redis

    利用Zabbix监控Redis Zabbix监控redis就比较简单了,因为zabbix官方提供了监控redis的模版和脚本,而且脚本有nodejs和python两种,下载地址:https://git ...

  3. MySQL数据库以及表的管理

    MySQL数据库以及表的管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 今天我们探讨的话题就是如何使用MySQL做开发,我们运维的主要工作不是去开发SQL的,但尽管如此,我们有 ...

  4. SQL语句(十二)分组查询

    (十二)分组查询 将数据表中的数据按某种条件分成组,按组显示统计信息 查询各班学生的最大年龄.最小年龄.平均年龄和人数 分组 SELECT <字段名表1> FROM <表名> ...

  5. 网络_OSI模型_数据包传输

    2017年1月12日, 星期四 网络_OSI模型_数据包传输 1.  网络_源主机_局域网_交换机_路由器_目标主机 2. OSI7七层_TCP/IP精简 OSI 7层:       应用层     ...

  6. atom使用markdown

    atom使用markdown 最近因为不喜欢博客园自身带的编辑工具,所以尝试使用markdown,目前只是想简单的把标题和内容分的开一些,以后在慢慢熟悉吧 1.atom上安装markdown 在没有外 ...

  7. centos6.5环境通过shell脚本备份php的web及mysql数据库并做远程备份容灾

    centos6.5环境通过shell脚本备份php的web及mysql数据库并做远程备份容灾 系统:centos6.5 1.创建脚本目录 mkdir -p /usr/local/sh/ 创建备份web ...

  8. git命令之git stash 暂存临时代码

    git stash — 暂存临时代码   stash命令可以很好的解决这样的问题.当你不想提交当前完成了一半的代码,但是却不得不修改一个紧急Bug,那么使用’Git stash’就可以将你当前未提交到 ...

  9. On the Bias/Variance tradeoff in Machine Learning

    参考:https://codesachin.wordpress.com/2015/08/05/on-the-biasvariance-tradeoff-in-machine-learning/ 之前一 ...

  10. CSU 1948: 超级管理员(普通费用流&&zkw费用流)

    Description 长者对小明施加了膜法,使得小明每天起床就像马丁的早晨一样. 今天小明早上醒来发现自己成了一位仓管员.仓库可以被描述为一个n × m的网格,在每个网格上有几个箱子(可能没有).为 ...