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详解的更多相关文章

  1. IEnumerator:概念详解

    IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象.IEnumerator对象有什么呢?它是一个真正的 ...

  2. IEnumerable和IEnumerator 详解 分类: C# 2014-12-05 11:47 18人阅读 评论(0) 收藏

    原:<div class="article_title"> <span class="ico ico_type_Original">&l ...

  3. IEnumerable<T> 接口和GetEnumerator 详解

    IEnumerable<T> 接口 .NET Framework 4.6 and 4.5   公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代. 若要浏览此类型的.NET Frame ...

  4. Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用

    一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...

  5. C# IEnumerator的详解

    迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式.简单来说,迭代器模式使得你能够获取到序列中的所有元素而 ...

  6. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  7. C# LINQ详解(转)

    C# LINQ详解(一)   原文标题:How does it work in C#?-Part 3 (C# LINQ in detail),作者:Mohammand A Rahman. 目录 LIN ...

  8. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  9. Delphi TStringHelper用法详解

    Delphi TStringHelper用法详解 (2013-08-27 22:45:42) 转载▼ 标签: delphi_xe5 it 分类: Delphi Delphi XE4的TStringHe ...

随机推荐

  1. Phabricator 在 centos 系统下发送 Email的配置

    前言 phabricator 配置email 其实很简单,配好smtp 服务器.端口.协议.用户名和登陆密码,但过程却好麻烦. 开始时跟着官网配 sendmail ,又 google 又 baidu, ...

  2. Ubuntu16.4下QT配置opencv3.1+FFmpeg

    安装依赖环境 sudo apt-get install build-essential sudo apt-get install cmake git libgtk2.0-dev pkg-config ...

  3. 初识Redux Middleware

    前言 原先改变store是通过dispatch(action) = > reducer:那Redux的Middleware是什么呢?就是dispatch(action) = > reduc ...

  4. [文章存档]Azure上部署的java app在向第三方服务传送中文时出现乱码

    https://docs.azure.cn/zh-cn/articles/azure-operations-guide/app-service-web/aog-app-service-web-java ...

  5. GTX1060 深度学习工具链

    通过试错,推荐GTX1060 WIN10的工具链记录如下: GPU: GTX 1060 6G OS: WIN10 CUDA:9.0 CuDNN:7.1.3 Tensorflow: Tensorflow ...

  6. one team

    Double H Team 1.队员 王熙航211606379(队长) 李冠锐211606364 曾磊鑫211606350 戴俊涵211606359 聂寒冰211606324 杨艺勇211606342 ...

  7. wordpress学习四: 一个简单的自定义主题

    在学习三里分析了自带的一个例子,本节我们就自己仿照他做个简单的吧,重点是调用wordpress封装好的函数和类,css和html可以稍好在调整. 将wp带的例子复制一份处理,重新名个名字. 清空ind ...

  8. someday团队Postmortem(事后诸葛亮会议)

    一.会议相关介绍: 时间:2018年1月12日 地点:第九实验楼五楼机房 参会人员:someday团队全体成员 二.每个成员在beta阶段的实践和alpha阶段有何改进? (一)设想和目标: 我们的软 ...

  9. 基于SSH框架的网上书店系统开发的质量属性

    基于SSH框架的网上书店系统开发的质量属性 对于我的基于SSH框架的网上书店系统的开发要实现的质量属性有可用性.可修改性.性能.安全性.易用性和可测试性. 1.对于可用性方面的战术: 可用性(Avai ...

  10. R-CNN阅读笔记

    论文地址:<Rich feature hierarchies for accurate object detection and semantic segmentation> 论文包含两个 ...