IEnumerator和IEnumerable详解
IEnumerator和IEnumerable
从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思。
了解了两个接口代表的含义后,接着看源码:
IEnumerator:
public interface IEnumerator
{
// Interfaces are not serializable
// Advances the enumerator to the next element of the enumeration and
// returns a boolean indicating whether an element is available. Upon
// creation, an enumerator is conceptually positioned before the first
// element of the enumeration, and the first call to MoveNext
// brings the first element of the enumeration into view.
//
bool MoveNext();
// Returns the current element of the enumeration. The returned value is
// undefined before the first call to MoveNext and following a
// call to MoveNext that returned false. Multiple calls to
// GetCurrent with no intervening calls to MoveNext
// will return the same object.
//
Object Current {
get;
}
// Resets the enumerator to the beginning of the enumeration, starting over.
// The preferred behavior for Reset is to return the exact same enumeration.
// This means if you modify the underlying collection then call Reset, your
// IEnumerator will be invalid, just as it would have been if you had called
// MoveNext or Current.
//
void Reset();
}
IEnumerable:
public interface IEnumerable
{
// Interfaces are not serializable
// Returns an IEnumerator for this enumerable Object. The enumerator provides
// a simple way to access all the contents of a collection.
[Pure]
[DispId(-4)]
IEnumerator GetEnumerator();
}
发现IEnumerable只有一个GetEnumerator函数,返回值是IEnumerator类型,从注释我们可以得知IEnumerable代表继承此接口的类可以获取一个IEnumerator来实现枚举这个类中包含的集合中的元素的功能(比如List<T>,ArrayList,Dictionary等继承了IEnumeratble接口的类)。
用foreach来了解IEnumerable,IEnumerator的工作原理
我们模仿ArrayList来实现一个简单的ConstArrayList,然后用foreach遍历。
//一个常量的数组,用于foreach遍历
class ConstArrayList : IEnumerable
{
public int[] constItems = new int[] { 1, 2, 3, 4, 5 };
public IEnumerator GetEnumerator()
{
return new ConstArrayListEnumeratorSimple(this);
}
}
//这个常量数组的迭代器
class ConstArrayListEnumeratorSimple : IEnumerator
{
ConstArrayList list;
int index;
int currentElement;
public ConstArrayListEnumeratorSimple(ConstArrayList _list)
{
list = _list;
index = -1;
}
public object Current
{
get
{
return currentElement;
}
}
public bool MoveNext()
{
if(index < list.constItems.Length - 1)
{
currentElement = list.constItems[++index];
return true;
}
else
{
currentElement = -1;
return false;
}
}
public void Reset()
{
index = -1;
}
}
class Program
{
static void Main(string[] args)
{
ConstArrayList constArrayList = new ConstArrayList();
foreach(int item in constArrayList)
{
WriteLine(item);
}
ReadKey();
}
}
输出结果:
1
2
3
4
5
代码达到了遍历效果,但是在用foreach遍历时,IEnumerator和IEnumerable究竟是如何运行的,我们可以通过增加增加日志可以直观的看到原因。
//一个常量的数组,用于foreach遍历
class ConstArrayList : IEnumerable
{
public int[] constItems = new int[] { 1, 2, 3, 4, 5 };
public IEnumerator GetEnumerator()
{
WriteLine("GetIEnumerator");
return new ConstArrayListEnumeratorSimple(this);
}
}
//这个常量数组的迭代器
class ConstArrayListEnumeratorSimple : IEnumerator
{
ConstArrayList list;
int index;
int currentElement;
public ConstArrayListEnumeratorSimple(ConstArrayList _list)
{
list = _list;
index = -1;
}
public object Current
{
get
{
WriteLine("Current");
return currentElement;
}
}
public bool MoveNext()
{
if(index < list.constItems.Length - 1)
{
WriteLine("MoveNext true");
currentElement = list.constItems[++index];
return true;
}
else
{
WriteLine("MoveNext false");
currentElement = -1;
return false;
}
}
public void Reset()
{
WriteLine("Reset");
index = -1;
}
}
class Program
{
static void Main(string[] args)
{
ConstArrayList constArrayList = new ConstArrayList();
foreach(int item in constArrayList)
{
WriteLine(item);
}
ReadKey();
}
}
输出结果:
GetIEnumerator
MoveNext true
Current
1
MoveNext true
Current
2
MoveNext true
Current
3
MoveNext true
Current
4
MoveNext true
Current
5
MoveNext false
通过输出结果,我们可以发现,foreach在运行时会先调用ConstArrayList的GetIEnumerator函数获取一个ConstArrayListEnumeratorSimple,之后通过循环调用ConstArrayListEnumeratorSimple的MoveNext函数,index后移,更新Current属性,然后返回Current属性,直到MoveNext返回false。
总结一下:
GetIEnumerator()负责获取枚举器。
MoveNext()负责让Current获取下一个值,并判断遍历是否结束。
Current负责返回当前指向的值。
Rest()负责重置枚举器的状态(在foreach中没有用到)
这些就是IEnumerable,IEnumerator的基本工作原理了。
其次我们发现:
ConstArrayList constArrayList = new ConstArrayList();
foreach(int item in constArrayList)
{
writeLine(item);
}
其实就等价于:
ConstArrayList constArrayList = new ConstArrayList();
IEnumerator enumeratorSimple = constArrayList.GetEnumerator();
while (enumeratorSimple.MoveNext())
{
int item = (int)enumeratorSimple.Current;
WriteLine(item);
}
也就是说foreach其实是一种语法糖,用来简化对可枚举元素的遍历代码。而被遍历的类通过实现IEnumerable接口和一个相关的IEnumerator枚举器来实现遍历功能。
IEnumerator和IEnumerable详解的更多相关文章
- IEnumerator:概念详解
IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象.IEnumerator对象有什么呢?它是一个真正的 ...
- IEnumerable和IEnumerator 详解 分类: C# 2014-12-05 11:47 18人阅读 评论(0) 收藏
原:<div class="article_title"> <span class="ico ico_type_Original">&l ...
- IEnumerable<T> 接口和GetEnumerator 详解
IEnumerable<T> 接口 .NET Framework 4.6 and 4.5 公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代. 若要浏览此类型的.NET Frame ...
- Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...
- C# IEnumerator的详解
迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式.简单来说,迭代器模式使得你能够获取到序列中的所有元素而 ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- C# LINQ详解(转)
C# LINQ详解(一) 原文标题:How does it work in C#?-Part 3 (C# LINQ in detail),作者:Mohammand A Rahman. 目录 LIN ...
- C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield
IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...
- Delphi TStringHelper用法详解
Delphi TStringHelper用法详解 (2013-08-27 22:45:42) 转载▼ 标签: delphi_xe5 it 分类: Delphi Delphi XE4的TStringHe ...
随机推荐
- Phabricator 在 centos 系统下发送 Email的配置
前言 phabricator 配置email 其实很简单,配好smtp 服务器.端口.协议.用户名和登陆密码,但过程却好麻烦. 开始时跟着官网配 sendmail ,又 google 又 baidu, ...
- Ubuntu16.4下QT配置opencv3.1+FFmpeg
安装依赖环境 sudo apt-get install build-essential sudo apt-get install cmake git libgtk2.0-dev pkg-config ...
- 初识Redux Middleware
前言 原先改变store是通过dispatch(action) = > reducer:那Redux的Middleware是什么呢?就是dispatch(action) = > reduc ...
- [文章存档]Azure上部署的java app在向第三方服务传送中文时出现乱码
https://docs.azure.cn/zh-cn/articles/azure-operations-guide/app-service-web/aog-app-service-web-java ...
- GTX1060 深度学习工具链
通过试错,推荐GTX1060 WIN10的工具链记录如下: GPU: GTX 1060 6G OS: WIN10 CUDA:9.0 CuDNN:7.1.3 Tensorflow: Tensorflow ...
- one team
Double H Team 1.队员 王熙航211606379(队长) 李冠锐211606364 曾磊鑫211606350 戴俊涵211606359 聂寒冰211606324 杨艺勇211606342 ...
- wordpress学习四: 一个简单的自定义主题
在学习三里分析了自带的一个例子,本节我们就自己仿照他做个简单的吧,重点是调用wordpress封装好的函数和类,css和html可以稍好在调整. 将wp带的例子复制一份处理,重新名个名字. 清空ind ...
- someday团队Postmortem(事后诸葛亮会议)
一.会议相关介绍: 时间:2018年1月12日 地点:第九实验楼五楼机房 参会人员:someday团队全体成员 二.每个成员在beta阶段的实践和alpha阶段有何改进? (一)设想和目标: 我们的软 ...
- 基于SSH框架的网上书店系统开发的质量属性
基于SSH框架的网上书店系统开发的质量属性 对于我的基于SSH框架的网上书店系统的开发要实现的质量属性有可用性.可修改性.性能.安全性.易用性和可测试性. 1.对于可用性方面的战术: 可用性(Avai ...
- R-CNN阅读笔记
论文地址:<Rich feature hierarchies for accurate object detection and semantic segmentation> 论文包含两个 ...