设计模式之三:迭代器模式(IteratorPattern)
迭代器(Iterator)模式,又叫游标(Cursor)模式。其定义为:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式是和容器相关的,对容器对象的访问设计到遍历算法。
迭代器模式由以下角色组成:
- 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
- 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并记录遍历中的当前位置。
- 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
- 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口---这个迭代器角色与该容器的结构相关。
意图:就是提供一种访问集合对象的方法,而又无需暴露该对象的内部细节。
在.NET中的Iterator模式
在.NET下实现的Iterator模式,对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色。它的代码如下:
public interface IEnumerator
{
object Current
{
get;
}
void Reset();
bool MoveNext();
}
IEnumerable则扮演的就是抽象聚集的角色,只有一个GetEnumertor()方法。如果集合对象需要具备迭代遍历的功能,就必须实现该接口。
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
下面看一个在网上http://www.theserverside.net/看到的.NET1.1下的迭代器例子,我稍微做了修改,Person类是一个可枚举的类。PersonsEnumerator类是一个枚举器类
public class Persons : IEnumerable
{
public string[] m_Names;
public Persons(string[] names)
{
m_Names = new string[names.Length];
names.CopyTo(m_Names, 0);
}
private string this[int index]
{
get
{
return m_Names[index];
}
set
{
m_Names[index] = value;
}
}
public IEnumerator GetEnumerator()
{
return new PersonsEnumerator(this);
}
}
public class PersonsEnumerator : IEnumerator
{
private int index = -1;
private Persons p;
public PersonsEnumerator(Persons p)
{
this.p = p;
}
public bool MoveNext()
{
if (index < p.m_Names.Length)
{
return true;
}
return false;
}
public void Reset()
{
index = -1;
}
public object Current
{
get
{
return p.m_Names[index];
}
}
}
在.NET2.0及以后的版本,由于有了yield return关键字,实现起来就更简单了:
public class Persons : IEnumerable
{
private string[] m_names;
public Persons(string[] names)
{
m_names = new string[names.Length];
names.CopyTo(m_names,0);
}
public IEnumerator GetEnumerator()
{
foreach(string name in m_names)
{
yield return name;
}
}
}
另外顺便简单介绍一下yield关键字:
关键字yield,在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。迭代器有两个特殊语句:yield return <expression>;
Yield break;
迭代器块
迭代器块是有一个或多个yield语句的代码库。下面三种类型的代码块中任意一种都可以是迭代器块:
方法主体
访问器主体
运算符主体
Yield语句只能出现在迭代器块中,该块可用作方法,运算符或访问器的体。这类方法,运算符或访问器的体受以下约束的控制:
- 不允许不安全块
- 方法,运算符或访问器的参数不能是ref或out
- Yield语句不能出现在匿名方法中
- Yield return语句不能出在catch块中活含有一个或多个Catch子句的的try块中。
Yield语句的跌代块可以产生IEnumerator和IEnumerable两种对象: