概念:迭代器(iterator),也称光标(cursor),是程序设计的软件设计模式,迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的标识。
从表现效果上看,迭代器是可以在容器对象(例如链表或数组)上遍历访问的接口,设计人员不需要关心容器对象的内存分配的实现细节。一般可以用foreach遍历的类,都是实现了迭代器的。
标准迭代器的实现方法
继承接口:IEnumerator,IEnumerable
可以同时继承IEnumerator和IEnumerable实现其中的方法。
实例:
class CustomList : IEnumerable, IEnumerator
{
public int[] list;
//光标从-1开始,表示数据到达的位置。
public int position = -1;
public CustomList()
{
list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumeratable
public IEnumerator GetEnumerator()
{
Reset();
return this;
}
#endregion
#region IEnumerator
public object Current
{
get
{
return list[position];
}
}
public bool MoveNext()
{
//移动光标
++position;
//判断光标位置是否溢出,溢出就不合法。
return position < list.Length;
}
//reset是重置光标位置,一般在获取IEnumerator对象这个函数中实现,
//用于第一次重置光标位置。
public void Reset()
{
position = -1;
}
#endregion
}
用yield return语法糖实现迭代器
yield return语法糖是C#提供的语法糖。
语法糖,也称糖衣语法,主要作用是将复杂逻辑简单化,可以增加程序的可读性,从而减少程序代码出错的机会。
接口:IEnumerable
实例:
class CustomList2 : IEnumerable
{
private int[] list;
public CustomList2()
{
list = new int[] { 1,2,3,4,5,6,7,8 };
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < list.Length; i++)
{
//关键字yield,配合迭代器IEnumerator使用
//可以理解为暂时返回,保留当前状态。
yield return list[i];
}
}
}
用yield return语法糖为泛型类实现迭代器
实例:
class CustomList3<T> : IEnumerable
{
private T[] listArray;
public CustomList3(params T[] listArray)
{
this.listArray = listArray;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < listArray.Length; i++)
{
//关键字yield,配合迭代器IEnumerator使用
//可以理解为暂时返回,保留当前状态。
yield return listArray[i];
}
}
}
foreach本质
-
获取in后面对象的IEnumerator,默认调用对象中的GetEnumerator方法,不继承IEnumerator接口,只实现GetEnumerator方法,foreach也不会报错。
-
执行得到的IEnumerator对象中的MoveNext方法,把光标移动到下一个位置;
-
当MoveNext方法的返回值是true时,将对应光标的值传给Current,然后返回给item;
-
一次foreach循环后,执行Reset方法将光标位置归位-1,Reset方法一般在获取IEnumerator对象这个函数中实现;
-
当MoveNext方法的返回值是false时,终止循环。
总结
迭代器是通过foreach在外部遍历对象中元素的同时,不用了解对象内部结构。
有两种实现的方法:一种是继承IEnumerable和IEnumerator接口,并实现接口中的方法实现;另一种是继承IEnumerable接口和使用yield return语法糖返回内容。