此外,由於每個迭代程序都保持單獨的迭代狀態 ,所以多個客戶端可以執行單獨的並發迭代。通過實現 IEnumerable,諸如數組 和隊列這樣的數據結構可以支持這種超常規的迭代。在 foreach 循環中生成的代 碼調用類的 GetEnumerator 方法簡單地獲得一個 IEnumerator 對象,然後將其 用於 while 循環,從而通過連續調用它的 MoveNext 方法和當前屬性遍歷集合。 如果您需要顯式地遍歷集合,您可以直接使用 IEnumerator(不用求助於 foreach 語句)。
但是使用這種方法有一些問題。首先,如果集合包含值 類型,則需要對它們進行裝箱和拆箱才能獲得項,因為 IEnumerator.Current 返 回一個對象。這將導致潛在的性能退化和托管堆上的壓力增大。即使集合包含引 用類型,仍然會產生從對象向下強制類型轉換的不利結果。雖然大多數開發人員 不熟悉這一特性,但是在 C# 1.0 中,實際上不必實現 IEnumerator 或 IEnumerable 就可以為每個循環實現迭代程序模式。編譯器將選擇調用強類型化 版本,以避免強制類型轉換和裝箱。結果是,即使在 1.0 版本中,也可能沒有導 致性能損失。
為了更好地闡明這個解決方案並使其易於實現,Microsoft .Net 框架 2.0 在 System.Collections.Generics 命名空間中定義了一般的類型 安全的 IEnumerable <ItemType> 和 IEnumerator <ItemType> 接 口:
public interface IEnumerable<ItemType>
{
IEnumerator<ItemType> GetEnumerator();
}
public interface IEnumerator<ItemType> : IDisposable
{
ItemType Current{get;}
bool MoveNext();
}
除 了利用泛型之外,新的接口與其前身還略有差別。與 IEnumerator 不同, IEnumerator <ItemType> 是從 IDisposable 派生而來的,並且沒有 Reset 方法。圖 2 中的代碼顯示了實現 IEnumerable <string> 的簡單 city 集合,而圖 3 顯示了編譯器在跨越 foreach 循環的代碼時如何使用該接口 。圖 2 中的實現使用了名為 MyEnumerator 的嵌套類,它將一個引用作為構造參 數返回給要枚舉的集合。MyEnumerator 清楚地知道 city 集合(本例中的一個數 組)的實現細節。MyEnumerator 類使用 m_Current 成員變量維持當前的迭代狀 態,此成員變量用作數組的索引。
Figure 2Implementing IEnumerable<string>
public class CityCollection : IEnumerable<string>
{
string[] m_CitIEs = {"New York","Paris","London"};
public IEnumerator<string> GetEnumerator()
{
return new MyEnumerator(this);
}
//Nested class definition
class MyEnumerator : IEnumerator<string>
{
CityCollection m_Collection;
int m_Current;
public MyEnumerator(CityCollection collection)
{
m_Collection = collection;
m_Current = -1;
}
public bool MoveNext()
{
m_Current++;
if(m_Current < m_Collection.m_CitIEs.Length)
return true;
else
return false;
}
public string Current
{
get
{
if(m_Current == -1)
throw new InvalidOperationException();
return m_Collection.m_CitIEs[m_Current];
}
}
public void Dispose(){}
}
}
圖 2