C# 程式設計
產品編號:F8333
定價:580元
黃昕暐 譯
|
|
本期書摘--改良列舉器
在前一小節中的列舉器有兩個缺點。
首先, 這個列舉器不能在編譯時期確認型別的正確性, 而必
須留待執行時期才能確認, 如果您撰寫這樣的程式:
IntList intList = new IntList();
intList.Add(55);
//
foreach (string s in intList)
{
}
其中的錯誤就沒有辦法在編譯時期偵測出來, 但是在執行時
會引發例外。編譯器之所以沒有辦法檢查出這種錯誤, 是因
為 IEnumerato.Current 的型別是 object, 而由 object
轉型為 string 卻是合法的動作。
Current 是 object 型別的作法還會引發第二個問題, 就是
當傳回資料值型別時會進行裝箱的動作, 可是這顯然是無端
的浪費, 因為 IntListEnumerator.Current 會將 int 裝箱
, 但是一經過屬性取得後就又立即拆箱了。
為了解決這兩個問題, C# 在處理列舉器時採用了樣式比對的
方式, 而不是嚴格要求實作完全相符的介面。C# 並不要求您
實作 IEnumerable 介面, 只要實作有 GetEnumerator ()函
式即可, 而且這個函式也不必要傳回一個 IEnumerator 物件
, 而可以傳回特定類別的物件作為列舉器, 只要這個物件實
作有 MoveNext ()、Reset ()、以及 Current, 而且
Current 也不必是 object 型別。
有了這樣的調整後, 嚴格型別的集合類別就可以在編譯時期
檢查型別的正確性, 而資料值型別也不再需要裝箱了。要為
剛剛的範例加上這樣的修改並不難, 首先介面名稱已經從類
別實作的清單中移除了, 而 IntList.GetEnumerator () 函
式也改成這樣:
public IntListEnumerator GetEnumerator()
{
return(new IntListEnumerator(this));
}
而IntListEnumerator.Current也只要小小的修改:
public int Current
{
get
{
if (revision != intList.Revision)
throw new InvalidOperationException
("Collection modified while enumerating.");
return(intList[index]);
}
}
這樣就完成了。
不過,還有一個問題。由於實作列舉器的標準方式還是透過
IEnumerable 以及 IEnumerator介面, 所以依循這個標準的
語言就沒有辦法列舉 IntList 集合了。
解決的方法是以強制實作介面1 的方式實作這兩個介面,
也就是說, 為 IntList 強制實作
IEnumerable.GetEnumerator ()函式:
public IEnumerator IEnumerable.GetEnumerator()
{
return(GetEnumerator());
}
並且為IntListEnumerator強制實作IEnumerator.Current:
public object IEnumerator.Current
{
get
{
return(Current);
}
}
這樣就可以支援標準的循序處理方法, 同時可以適用在支
援嚴格型別而採取樣式比對方式的編譯器, 以及僅支援
IEnumerable/IEnumerator介面的編譯器了。
-- 待續
|