动机:在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素(可复用类库ClassLibrary的作者);同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式 。
意图:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
基本Code:
//C# 1.0,使用IEnumerable和IEnumerator接口构建迭代器
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
Object Current
{
get;
}
bool MoveNext();
void Reset();
}
public class MyCollection:IEnumerable
{
int[] items;
public MyCollection()
{
items = new int[5] { 1, 2, 3, 4, 5 };
}
private class MyEnumerator:IEnumerator
{
int nIndex ;
MyCollection collection;
public MyEnumerator(MyCollection col)
{
this.collection = col;
nIndex = -1;
}
#region IEnumerator 成员
public object Current
{
get { return collection.items[nIndex]; }
}
public bool MoveNext()
{
nIndex++;
return (nIndex < collection.items.GetLength(0));
}
public void Reset()
{
nIndex = 0;
}
#endregion
}
public IEnumerator GetEnumerator()
{
return new MyEnumerator(this);
}
}
//为不同的算法提供了一个统一的接口
public class SomeType
{
//..
public int Sum(IEnumerable enumerable) //求和
{
IEnumerator ietor = enumerable.GetEnumerator();
int sum = 0;
foreach (int i in enumerable)
{
sum += i;
}
return sum;
}
//...
}
public class App
{
public static void Main()
{
MyCollection col = new MyCollection();
//可以直接使用foreach来进行遍历
//foreach (int i in col)
//{
// Console.WriteLine(i);
//}
//完全抽象于(不依赖于)具体集合结构的访问操作
IEnumerator ietor = col.GetEnumerator();
while (ietor.MoveNext())
{
int i = (int)ietor.Current;
//ietor.Current=100,可以,但是C#禁止--更改元素的值
//ietor.Remove(i),绝对禁止--更改结果
//i=100,没有更改效果
Console.WriteLine(i);
}
}
}
Iterator结构图:
Iterator模式要点:
1、 迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
2、 迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
3、 迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
//C# 2.0
public class MyCollection:IEnumerable
{
int[] items;
public MyCollection()
{
items = new int[5] { 1, 2, 3, 4, 5 };
}
private class MyEnumerator : IEnumerator
{
int nIndex;
MyCollection collection;
public MyEnumerator(MyCollection col)
{
this.collection = col;
nIndex = -1;
}
#region IEnumerator 成员
public object Current
{
get { return collection.items[nIndex]; }
}
public bool MoveNext()
{
nIndex++;
return (nIndex < collection.items.GetLength(0));
}
public void Reset()
{
nIndex = 0;
}
#endregion
}
//2.0的方式用yield
public IEnumerator GetEnumerator()
{
for (int i = 0; i < items.Length; i++)
{
yield return items[i];
}
}
}
public class App
{
public static void Main()
{
MyCollection col = new MyCollection();
IEnumerator ietor = col.GetEnumerator();
while (ietor.MoveNext())
{
int i = (int)ietor.Current;
Console.WriteLine(i);
}
}
}