迭代器(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兩種對象: