迭代器(Iterator)是最简单和最经常用到的模式之一。迭代器模式允许无需了解数据的内部表示的情况下,使用一个标准的接口来遍历该数据集合。总之,迭代器是为了方便的进行数据遍历用的,可以在不了解数据的具体数据结构的或者数据的内部组织结构的时候提供一个方便的遍历数据接口。一个个的返回数据。
迭代器很有用处,因为其提供了一种预先定义好的方式来遍历数据元素集合而又无需暴漏类内部放生的事情。
C#中IEnumerator接口中定义了如下的一些函数:
bool MoveNext();
void Reset();
object Current { get; };
这种枚举方法的一个缺点是,C#是强类型语言,这样的话object Current { get; }属性不能把对象作为集合中的数据的实际类型数据返回,这里返回的都是object类型,我们在实际的使用过程中还需要进行强制类型转换。这一点操作比较麻烦。
一个迭代器的示例。
对游泳选手的遍历,通过读取文件,然后构建一个游泳选手的数组,在主程序中通过对这个数据的遍历,完成列表数据的添加。
一个保存游泳选手基本信息的类:
using System;
using CsharpPats;
using System.Collections ;
namespace SimpleIterator
{
/// <summary>
/// Summary description for Kid.
/// </summary>
public class Kid
{
private string frname, lname, club;
private int age;
private float time;
private Hashtable hash;
public Kid(string line) {
hash = new Hashtable ();
StringTokenizer tok = new StringTokenizer (line);
string lnum = tok.nextToken ();
frname = tok.nextToken ();
//hash.Add (ParseVar.FRNAME , frname);
lname = tok.nextToken ();
//hash.Add (ParseVar.LNAME , lname);
age = Convert.ToInt32 ( tok.nextToken ());
//hash.Add (ParseVar.AGE , age);
club = tok.nextToken ();
//hash.Add (ParseVar.CLUB , club);
time = Convert.ToSingle (tok.nextToken ());
//hash.Add (ParseVar.TIME , time);
}
//-------
public object getData(int key) {
return hash[key];
}
//-----
public bool compare(Kid kd, int key) {
return compareTo(getData(key) , kd.getData (key));
}
//-----
private bool compareTo(object o1, object o2) {
if (o1.GetType().Name.Equals ("String") )
return compare_To((string)o1, (string) o2);
else
return compare_To((float)o1, (float)o2);
}
private bool compare_To(String s1, String s2) {
return s1.CompareTo (s2) >0;
}
private bool compare_To(int k1, int k2){
return k1 > k2;
}
private bool compare_To(float f1, float f2) {
return f1 > f2;
}
public string getFrname() {
return frname;
}
public string getLname() {
return lname;
}
public int getAge() {
return age;
}
public string getClub() {
return club;
}
}
}
保存所有选手信息的类,这个类从文件中读取数据,并构建一个游泳选手的数组:
using System;
using System.Collections ;
using CsharpPats;
namespace SimpleIterator
{
/// <summary>
/// Summary description for KidData.
/// </summary>
public class KidData :IEnumerator {
private ArrayList kids;
private int index;
public KidData(string filename) {
kids = new ArrayList ();
csFile fl = new csFile (filename);
fl.OpenForRead ();
string line = fl.readLine ();
while(line != null) {
Kid kd = new Kid (line);
kids.Add (kd);
line = fl.readLine ();
}
fl.close ();
index = 0;
}
//------
public bool MoveNext() {
index++;
return index < kids.Count ;
}
//------
public object Current {
get {
return kids[index];
}
}
//------
public void Reset() {
index = 0;
}
//------
public Kid[] getData() {
Kid[] kds = new Kid [kids.Count ];
for(int i=0; i< kids.Count ; i++ ) {
kds[i] = (Kid)kids[i];
}
return kds;
}
//-----
public int size() {
return kids.Count ;
}
//-----
public Kid getKid(int i) {
if(i>=0 && i< kids.Count )
return (Kid)kids[i];
else
return null;
}
//-----
public KidIterator getIterator() {
return new KidIterator (kids);
}
}
}
这个类实现了IEnumerator接口,并提供了一个返回迭代器的函数getIterator();
主程序中我们就可以直接使用MoveNext()函数完成对数据的遍历。
kids=new KidData("50free.txt");
while(kids.MoveNext())
{
Kid kd=(Kid)kids.Current;
lsKids.Items.Add("...");
}
通过Current属性返回枚举数据。
另一种更加灵活一点的操纵类中迭代器的方式是,给类提供一个getIterator方法,该方法返回类的迭代器的实例,这样的好处是,对于同一数据,可以让多个迭代器存在这一相同数据上,每个迭代器指向不同的数据。这些不同的迭代器可以同时工作。
这样需要重新设计一个迭代器类:
using System;
using System.Collections ;
namespace SimpleIterator
{
/// <summary>
/// Summary description for KidIterator.
/// </summary>
public class KidIterator : IEnumerator {
private ArrayList kids;
private int index;
public KidIterator(ArrayList kidz) {
kids = kidz;
index = 0;
}
//------
public bool MoveNext() {
index++;
return index < kids.Count ;
}
//------
public object Current {
get {
return kids[index];
}
}
//------
public void Reset() {
index = 0;
}
}
}
在KidData类中获取迭代器的时候就会返回一个迭代器的实例,getIterator函数。
虽然实现明确定义好遍历数据的方法是有优势的,但是定义过滤型的迭代器可以让迭代器在返回数据之前对数据进行一些计算操作或者筛选工作。
比如我们在通过迭代器构造了游泳选手列表之后,需要通过俱乐部在右侧的列表中显示不同俱乐部的选手。用户界面如图:
这样在下拉列表框中选择栏额俱乐部以后,会在迭代器中根据俱乐部名称进行筛选这个俱乐部的选手。
using System;
using System.Collections ;
namespace FileredIterator
{
/// <summary>
/// Summary description for KidIterator.
/// </summary>
public class FilteredIterator : IEnumerator {
private ArrayList kids;
private int index;
private string club;
public FilteredIterator(ArrayList kidz, string club) {
kids = kidz;
index = 0;
this.club = club;
}
//------
public bool MoveNext() {
bool more = index < kids.Count-1 ;
if(more) {
Kid kd = (Kid)kids[++index];
more = index < kids.Count;
while(more && ! kd.getClub().Equals (club)) {
kd = (Kid)kids[index++];
more = index < kids.Count ;
}
}
return more;
}
//------
public object Current {
get {
return kids[index];
}
}
//------
public void Reset() {
index = 0;
}
}
}
这里的KidData类做了一些改动:
using System;
using System.Collections ;
using CsharpPats;
namespace FileredIterator
{
/// <summary>
/// Summary description for KidData.
/// </summary>
public class KidData :IEnumerator {
private ArrayList kids;
private int index;
private Hashtable clubs;
public KidData(string filename) {
kids = new ArrayList ();
clubs = new Hashtable ();
csFile fl = new csFile (filename);
fl.OpenForRead ();
string line = fl.readLine ();
while(line != null) {
Kid kd = new Kid (line);
string club = kd.getClub ();
if(! clubs.Contains (club ) ) {
clubs.Add (club, club);
}
kids.Add (kd);
line = fl.readLine ();
}
fl.close ();
index = 0;
}
//------
public IDictionaryEnumerator getClubs() {
return clubs.GetEnumerator ();
}
//------
public bool MoveNext() {
index++;
return index < kids.Count ;
}
//------
public object Current {
get {
return kids[index];
}
}
//------
public void Reset() {
index = 0;
}
//------
public Kid[] getData() {
Kid[] kds = new Kid [kids.Count ];
for(int i=0; i< kids.Count ; i++ ) {
kds[i] = (Kid)kids[i];
}
return kds;
}
//-----
public int size() {
return kids.Count ;
}
//-----
public Kid getKid(int i) {
if(i>=0 && i< kids.Count )
return (Kid)kids[i];
else
return null;
}
//-----
public KidIterator getIterator() {
return new KidIterator (kids);
}
//----
public FilteredIterator getFilteredIterator(string club) {
return new FilteredIterator (kids, club);
}
}
}
这时候KidData类返回的就是新的过滤性迭代器了:
//----
public FilteredIterator getFilteredIterator(string club) {
return new FilteredIterator (kids, club);
}
KidDatal 类在构造的时候建立一个不重复的club的hashtable, 通过遍历这个hashtable可以构建俱乐部的下拉列表。
然后构造主窗体:
private void init() {
kdata = new KidData("50free.txt");
KidIterator kiter = kdata.getIterator ();
while(kiter.MoveNext() ) {
Kid kd = (Kid)kiter.Current;
lsKids.Items.Add (kd.getFrname() +" "+kd.getLname ());
}
IDictionaryEnumerator clubiter = kdata.getClubs ();
while(clubiter.MoveNext ()) {
cbClubs.Items.Add ((string)clubiter.Value );
}
}
当有俱乐部被选中的时候,触发事件,这里通过过滤性迭代器遍历游泳选手,然后显示这个俱乐部的选手:
private void cbClubs_SelectedIndexChanged(object sender, System.EventArgs e) {
string club = (String)cbClubs.SelectedItem ;
FilteredIterator iter = kdata.getFilteredIterator ( club);
lsClubKids.Items.Clear ();
while(iter.MoveNext() ) {
Kid kd = (Kid) iter.Current;
lsClubKids.Items.Add (kd.getFrname() +" "+kd.getLname ());
}
}
迭代器存在的一个问题是,当你遍历某个集合的时候,可能某个元素被添加到这个集合中,或者某个元素被删除,或者某些元素被其他的线程或者函数所修改,这样你在迭代的时候可能少获取一个元素,或者对于某个元素迭代两次,或者获取的数据是旧的修改之前的数据。对于这种问题,没有简单的解决方式,或许可以通过加锁的方式来防止数据被以外修改。