IEnumerable<T>
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>的更多相关文章
- 先说IEnumerable,我们每天用的foreach你真的懂它吗?
我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...
- 为IEnumerable<T>添加RemoveAll<IEnumerable<T>>扩展方法--高性能篇
最近写代码,遇到一个问题,微软基于List<T>自带的方法是public bool Remove(T item);,可是有时候我们可能会用到诸如RemoveAll<IEnumerab ...
- C# 索引器,实现IEnumerable接口的GetEnumerator()方法
当自定义类需要实现索引时,可以在类中实现索引器. 用Table作为例子,Table由多个Row组成,Row由多个Cell组成, 我们需要实现自定义的table[0],row[0] 索引器定义格式为 [ ...
- Entity Framework中使用IEnumerable<T>、IQueryable<T>及IList<T>的区别
1. IEnumerable<T> IEnumerable<T> :对于在内存中集合上运行的方法,返回的可枚举对象将捕获传递到方法的参数.在枚举该对象时,将使用查询运算符的逻辑 ...
- 你可能不知道的陷阱, IEnumerable接口
1. IEnumerable 与 IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...
- 通过IEnumerable和IDisposable实现可暂停和取消的任务队列
一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...
- 判断IEnumerable<T>集合中是否包含有T对象
比如,有角色集合中,只有用户创建有角色,才出现“分配”铵钮.反之,隐藏. IEnumerable有一个方法,叫Any:
- .NET面试题系列[11] - IEnumerable<T>的派生类
“你每次都选择合适的数据结构了吗?” - Jeffery Zhao .NET面试题系列目录 ICollection<T>继承IEnumerable<T>.在其基础上,增加了Ad ...
- .NET面试题系列[10] - IEnumerable的派生类
.NET面试题系列目录 IEnumerable分为两个版本:泛型的和非泛型的.IEnumerable只有一个方法GetEnumerator.如果你只需要数据而不打算修改它,不打算为集合插入或删除任何成 ...
- .NET面试题系列[9] - IEnumerable
.NET面试题系列目录 什么是IEnumerable? IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumera ...
随机推荐
- d3浅谈
d3是一个及其庞大的库,有20个模块,大小也达到了216kb,是JQ1.x的2倍多,JQ3.x的3倍多,JQ本来就挺笨重的一个库,d3更是如此,但是它的功能确实很强悍~ d3的定位是一个科学计算库,并 ...
- webpack:代码分割与按需加载
代码分割就是我们根据实际业务需求将代码进行分割,然后在合适的时候在将其加载进入文档中. 代码中总有些东西我们希望拆分开来,比如: 使用概率较低的模块,希望后期使用的时候异步加载 框架代码,希望能利用浏 ...
- POJ - 3436 ACM Computer Factory(最大流)
https://vjudge.net/problem/POJ-3436 题目描述: 正如你所知道的,ACM 竞赛中所有竞赛队伍使用的计算机必须是相同的,以保证参赛者在公平的环境下竞争.这就是所有这些 ...
- Liberty Mutual Property Inspection, Winner's Interview: Qingchen Wang
Liberty Mutual Property Inspection, Winner's Interview: Qingchen Wang The hugely popular Liberty Mut ...
- 如何将U盘转化成NTFS格式
拷贝数据到U盘,出现 出现错误0x80070052:无法创建目录或文件 然后发现应该是U盘为fat32格式的,感觉应该是这个问题 方法一:如果你是新买的U盘,或者U盘内数据已经备份到电脑,可以使用该条 ...
- springboot(五):springboot整合shiro-登录认证和权限管理
http://z77z.oschina.io/ http://www.cnblogs.com/aqsunkai/category/982003.html https://www.cnblogs.com ...
- python scrapy cookies 处理
def start_requests(self): cookies = 'anonymid=jcokuqwe................省略' # 首先是对cookies进行分割以;为节点 ook ...
- html5 canvas 奇怪的形状水平渐变(因为大多数的之前的文章把基础都打过了,所以开始写的快了,如果有不明白的,可以回顾下之前的)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- ASP.Net巧用窗体母版页
背景:每个网页的基本框架结构类似: 浏览网站的时候会发现,好多网站中,每个网页的基本框架都是一样的,比如,最上面都是网站的标题,中间是内容,最下面是网站的版权.开发提供商等信息: 在这些网页中,表头. ...
- 20155235 2016-2017-2 《Java程序设计》第8周学习总结
20155235 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第十四章 NIO与NIO2 认识NIO NIO概述 Channel架构与操作 Buffer架 ...