迭代器模式 与 C# IEnumerator/IEnumerable
Part1 迭代器模式 与 接口
IEnumerable
IEnumerator
interface IEnumerable
{
IEnumerator GetEnumerator();
}
// 泛型版本 : IEnumerator<T>
interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
这两个接口用于实现 迭代器 这种设计模式。
迭代器模式:
在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。
迭代器模式是一种行为设计模式,简单而言,就是将对集合的遍历有“外部控制”变为“内部控制”,将其封装起来。
数组就是将遍历完全交由外部处理。
Iterator模式的几个要点
- 迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
- 迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
- 迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。(所以 C# 中在 foreach 操作时,不允许更改集合,如果外部有更改,则会报错)。
Part2 foreach 语句的等价形式(while循环)
foreach(var p in Persons)
{
WriteLine(p);
}
// 等价于一个 while 循环
IEnumerator<Person> enumerator = persons.GetEnumerator();
while(enumerator.MoveNext())
{
Person p = enumerator.Current;
WriteLine(p);
}
- 可以看到,这里并没有调用
Reset
方法,此方法通常用于与 COM 的交互操作,许多 .NET 枚举器抛出 NotSupportedException; - 集合可以被 foreach, 不一定需要实现 IEnumerable 接口,有 GetEnumerator 方法即可。
- 一个集合类可以提供多个不同的 GetEnumerator 实现,如 GetEnumerator1,GetEnumerator2,返回不同的 IEnumerator,以实现不同的迭代功能。(见下文)
Part3 IEnumerator 与 yield
一个集合类想要支持被迭代,最主要的是构造一个 Enumerator 类,实现 IEnumerator 接口,在 GetEnumerator 方法中返回这个 Enumerator 类。
如此,在 Enumerator 类中,需要维护 Current 属性和 MoveNext 方法,在 MoveNext 方法中,更新 Current 的值,并返回是否还有后续值的 bool 判断。
在实现 IEnumerator 接口时,通常也要实现其泛型版本 IEnumerator。
这段文字看起来有点晕,实际上,实现一个 IEnumerator 也是一个苦力活。在实际的编程中,一般直接使用已有的集合元素,不必从头实现一个 IEnumerator 。
yield
是 C# 提供语法糖,可以方便的实现 IEnumerator 接口。如:
public IEnumerator<string> GetEnumerator()
{
yield return "A";
yield return "B";
yield return "C";
// ...
yield return "Z";
}
这样,实际上就实现了一个集合,这个集合保存了大写的26个字母。
yield return
语句返回集合的一个元素,并移动到下一个元素,相当于同时维护 Current
和 MoveNext
;yield break
可停止迭代。
使用 yield
,编译器会创建一个状态机,用于实际维护 Current
和 MoveNext
。
Part4 实现多个不同的 IEnumerator
有一个 MusicCollection 集合类,里面包含了 IList 集合,现在要在其中实现对 Music.Title , Music.Author , Music.Time 进行遍历的支持,可以这么做:
public class MusicCollection
{
private IList<Music> MusicList;
public MusicCollection(IList<Music> musicList)
{
MusicList = musicList;
}
public IEnumerator<string> GetTitleEnumerator()
{
for(int i=0;i<MusicList.Length;i++)
{
yield return MusicList[i].Title;
}
}
public IEnumerator<string> GetAuthorEnumerator()
{
for(int i=0;i<MusicList.Length;i++)
{
yield return MusicList[i].Author;
}
}
public IEnumerator<string> GetTimeEnumerator()
{
for(int i=0;i<MusicList.Length;i++)
{
yield return MusicList[i].Time;
}
}
}
// 外部调用:
pubic class Test
{
public void Test()
{
var musicList = new List<Music>();
var musicCollection = new MusicCollection(musicList);
foreach(string title in musicCollection.GetTitleEnumerator())
{
Console.WriteLine(title);
}
}
}
迭代器中还可以返回迭代器(嵌套),有趣的用法。
Part5 线程安全
迭代显然是非线程安全的,每次IEnumerable都会生成新的IEnumerator,从而形成多个互相不影响的迭代过程。
在迭代过程中,不能修改迭代集合,否则不安全。
参考资料
迭代器模式 与 C# IEnumerator/IEnumerable的更多相关文章
- 设计模式学习之迭代器模式(Iterator,行为型模式)(17)
参考地址:http://www.cnblogs.com/zhili/p/IteratorPattern.html 一.介绍迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作 ...
- C#设计模式-迭代器模式
一. 迭代器(Iterator)模式 迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作,同时也肯定支持遍历集合元素的操作,我们此时可以把遍历操作也放在集合对象中,但这样的 ...
- 迭代器模式的一种应用场景以及C#对于迭代器的内置支持
迭代器模式 先放上gof中对于迭代器模式的介绍镇楼 意图 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示. 别名 游标(Cursor). 动机 一个聚合对象, 如列表(li ...
- C#设计模式(16)——迭代器模式(Iterator Pattern)
一.引言 在上篇博文中分享了我对命令模式的理解,命令模式主要是把行为进行抽象成命令,使得请求者的行为和接受者的行为形成低耦合.在一章中,将介绍一下迭代器模式.下面废话不多说了,直接进入本博文的主题. ...
- C#设计模式——迭代器模式(Iterator Pattern)
一.概述在软件开发过程中,我们可能会希望在不暴露一个集合对象内部结构的同时,可以让外部代码透明地访问其中包含的元素.迭代器模式可以解决这一问题.二.迭代器模式迭代器模式提供一种方法顺序访问一个集合对象 ...
- .NET设计模式(18):迭代器模式(Iterator Pattern)(转)
概述 在面向对象的软件设计中,我们经常会遇到一类集合对象,这类集合对象的内部结构可能有着各种各样的实现,但是归结起来,无非有两点是需要我们去关心的:一是集合内部的数据存储结构,二是遍历集合内部的数据. ...
- [设计模式] Iterator - 迭代器模式:由一份奥利奥早餐联想到的设计模式
Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...
- C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
一.引言 今天我们开始讲"行为型"设计模式的第三个模式,该模式是[迭代器模式],英文名称是:Iterator Pattern.还是老套路,先从名字上来看看."迭代器模 ...
- [C# 设计模式] Iterator - 迭代器模式:我与一份奥利奥早餐的故事
Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...
随机推荐
- 回溯算法——解决n皇后问题
所谓回溯(backtracking)是通过系统地搜索求解问题的方法.这种方法适用于类似于八皇后这样的问题:求得问题的一个解比较困难,但是检查一个棋局是否构成解很容易. 不多说,放上n皇后的回溯问题代码 ...
- Docker 启动Centos
docker run -d -e "container=docker" --privileged=true -v /sys/fs/cgroup:/sys/fs/cgroup --n ...
- C# 中printDocument打印、预览、打印机设置和打印属性的方法
private void Form1_Load(object sender, System.EventArgs e) { //获取或设置一个值,该值指示是否发送到文件或端口 printDocument ...
- postman pre-request-script 操作方法记录
上代码----自己参考下就明白了 例子1:自动登陆获取token let chatHost,chatName,chatPassword;//设置环境变量 if (pm.environment.get( ...
- webrtc前景如何
首先WebRTC是什么? WebRTC --- Web browsers with Real-Time Communications (RTC). WebRTC是一个免费.开放的项目.使web浏览器通 ...
- HDU 4825 Xor Sum (裸字典树+二进制异或)
题目链接 Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将 ...
- POJ 1350 Cabric Number Problem (模拟)
题目链接 Description If we input a number formed by 4 digits and these digits are not all of one same va ...
- DockerFile指令集
FROM 语法:FROM <image>[:<tag>] 解释:设置要制作的镜像基于哪个镜像,FROM指令必须是整个Dockerfile ...
- fonts.googleapis.com 字体报错问题解决。
更多内容推荐微信公众号,欢迎关注: 无法加载这些字体是因为,google的网站在国内无法访问造成的.在AdminLTE.css 和AdminLte.less中有如下内容: @import url(ht ...
- Java学习笔记——继承、接口、多态
浮点数的运算需要注意的问题: BigDecimal operand1 = new BigDecimal("1.0"); BigDecimal operand2 = new BigD ...