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);
}
  1. 可以看到,这里并没有调用 Reset 方法,此方法通常用于与 COM 的交互操作,许多 .NET 枚举器抛出 NotSupportedException;
  2. 集合可以被 foreach, 不一定需要实现 IEnumerable 接口,有 GetEnumerator 方法即可。
  3. 一个集合类可以提供多个不同的 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 语句返回集合的一个元素,并移动到下一个元素,相当于同时维护 CurrentMoveNextyield break 可停止迭代。

使用 yield,编译器会创建一个状态机,用于实际维护 CurrentMoveNext

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

  1. 设计模式学习之迭代器模式(Iterator,行为型模式)(17)

    参考地址:http://www.cnblogs.com/zhili/p/IteratorPattern.html 一.介绍迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作 ...

  2. C#设计模式-迭代器模式

    一. 迭代器(Iterator)模式 迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作,同时也肯定支持遍历集合元素的操作,我们此时可以把遍历操作也放在集合对象中,但这样的 ...

  3. 迭代器模式的一种应用场景以及C#对于迭代器的内置支持

    迭代器模式 先放上gof中对于迭代器模式的介绍镇楼 意图 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示. 别名 游标(Cursor). 动机 一个聚合对象, 如列表(li ...

  4. C#设计模式(16)——迭代器模式(Iterator Pattern)

    一.引言 在上篇博文中分享了我对命令模式的理解,命令模式主要是把行为进行抽象成命令,使得请求者的行为和接受者的行为形成低耦合.在一章中,将介绍一下迭代器模式.下面废话不多说了,直接进入本博文的主题. ...

  5. C#设计模式——迭代器模式(Iterator Pattern)

    一.概述在软件开发过程中,我们可能会希望在不暴露一个集合对象内部结构的同时,可以让外部代码透明地访问其中包含的元素.迭代器模式可以解决这一问题.二.迭代器模式迭代器模式提供一种方法顺序访问一个集合对象 ...

  6. .NET设计模式(18):迭代器模式(Iterator Pattern)(转)

    概述 在面向对象的软件设计中,我们经常会遇到一类集合对象,这类集合对象的内部结构可能有着各种各样的实现,但是归结起来,无非有两点是需要我们去关心的:一是集合内部的数据存储结构,二是遍历集合内部的数据. ...

  7. [设计模式] Iterator - 迭代器模式:由一份奥利奥早餐联想到的设计模式

    Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...

  8. C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第三个模式,该模式是[迭代器模式],英文名称是:Iterator Pattern.还是老套路,先从名字上来看看."迭代器模 ...

  9. [C# 设计模式] Iterator - 迭代器模式:我与一份奥利奥早餐的故事

    Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...

随机推荐

  1. [转载]function与感叹号

    http://swordair.com/function-and-exclamation-mark/ 最近有空可以让我静下心来看看各种代码,function与感叹号的频繁出现,让我回想起2个月前我回杭 ...

  2. python核心编程笔记——Chapter5

    好吧,开始第五章习题: 5-2.又是老调重谈,raw_input这个函数真是非常麻烦,返回是str类型,没办法,只能在函数里面使用int内建.(就只是一道简单的两数相乘,哭了) #!/usr/bin/ ...

  3. asp.net WebForm程序删除.designer.cs文件之后的故事

    1.介绍 正常情况下添加一个WebForm程序结构如下(命名为:myWebForm.aspx) 文件说明:.aspx文件:书写html代码部分,以及javascript,css等代码书写及引用 .as ...

  4. 10 款非常棒的CSS代码格式化工具推荐

    如果你刚开始学习CSS,这意味着你的很多代码或结构可能需要优化,比如你可能过多使用了类.添加了多余的间隔或空行等等,这将导致代码臃肿.混乱,可读性和执行效率将大大降低. 本文为你整理了几款CSS工具, ...

  5. linux nginx服务 反向代理 负载均衡 nfs服务

    一.nginx服务 1.首先软件停用firewall #systemctl stop firewalld stop:本次停用 disable:开机停用 enable:开机启用 #ps aux | gr ...

  6. 【译】第六篇 Integration Services:初级工作流管理

    本篇文章是Integration Services系列的第六篇,详细内容请参考原文. 简介在前几篇文章中,我们关注使用增量加载方式加载数据.在本篇文章,我们将关注使用优先约束管理SSIS控制流中的工作 ...

  7. POI读取Excel数据保存到数据库,并反馈给用户处理信息(导入带模板的数据)

    今天遇到这么一个需求,将课程信息以Excel的形式导入数据库,并且课程编号再数据库中不能重复,也就是我们需要先读取Excel提取信息之后保存到数据库,并将处理的信息反馈给用户.于是想到了POI读取文件 ...

  8. solr配置中文分词器——(十二)

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqcAAAGzCAIAAACdKClDAAAgAElEQVR4nOydd5gUxdbGx5xASZKXLB

  9. 【codeforces】【比赛题解】#920 Educational CF Round 37

    [A]浇花 题意: 一个线段上每个整点都有花,有的点有自动浇花的喷水器,有问几秒能浇完所有的花. 题解: 大模拟 #include<cstdio> #include<cstring& ...

  10. torch.normal(means, std, out=None)

    返回满足正态分布的张量 means和std分别给出均值和标准差