5. 實現IEnumerable<KeyValuePair<TKey, TValue>>接口
我們先來看看ReversibleSortedList類的定義:
public class ReversibleSortedList<TKey, TValue> :
IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>,
IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable
它一共實現了6個接口。但從本質上來說,實現IDictionary<TKey, TValue>接口和IDictionary接口就等同於實現了以上6個接口。因為 IDictionary<TKey, TValue>繼承自 ICollection<KeyValuePair<TKey, TValue>>和 IEnumerable<KeyValuePair<TKey, TValue>>接口,而IDictionary 接口繼承自ICollection和IEnumerable接口。下面我們來看一下這些接口的關系 圖:
圖2 ReversibleSortedList類接口關系圖
其實 ReversibleSortedList類的大部份代碼都用於實現左邊這兩個接口,看到這張圖 你應該理解了為什麼會需要1300行的代碼。為了一步一步地實現這個類,我們需 要首先實現底層接口,由於ICollection接口本身就繼承自IEnumerable接口,所 以首先應該實現的是IEnumerable接口。從前面一節可以知道IEnumerable接口的 實現需要一個嵌套類,它返回一個枚舉器。我們可以為IDictionary<TKey, TValue>接口和IDictionary接口實現同一個枚舉器,只要這個枚舉器實現了 IEnumerator<KeyValuePair<K, V>>接口和IDictionaryEnumerator 接口就可以了。前者是IDictionary<TKey, TValue>接口所需要的枚舉器 ,後者是IDictionary接口所需要的枚舉器(見前一節)。下面是我們的這個枚 舉器所需實現的接口的關系圖:
圖3 Enumerator<K, V>類接口關系圖
實現這個類需要注 意一個問題,在ReversibleSortedList類中添加了一個新的成員變量:version 。在插入元素時(insert方法)會更改這個version值,表示類中的元素已經進 行了改動。為什麼要使用它呢?在此我做些推測。從MSDN中或許可以找到線索。 我們在MSDN找到關於Dictionary<TKey, TValue>類的介紹,這個類跟 ReversibleSortedList類非常相似。在關於它的線程安全中有這麼一段話:
此類型的公共靜態成員是線程安全的。但不能保證任何實例成員是線程 安全的。
只要不修改該集合,Dictionary<(Of <(TKey, TValue>)>) 就可以同時支持多個閱讀器。 即便如此,從頭到尾對一個集 合進行枚舉本質上並不是一個線程安全的過程。當出現枚舉與寫訪問互相爭用這 種極少發生的情況時,必須在整個枚舉過程中鎖定集合。若要允許多個線程訪問 集合以進行讀寫操作,則必須實現自己的同步。
可以推測,version用於 在枚舉過程中判斷是否有其他線程更改了ReversibleSortedList實例中存儲的元 素,以便彈出異常。這一點可以在稍後的代碼中看見。
這時可能有人會 問了,前面一節中關沒有這種判斷啊?好,讓我們看看上一節關於IDictionary 接口的代碼。在它的實現枚舉器的嵌套類SimpleDictionaryEnumerator中,看看 它的構造方法:
public SimpleDictionaryEnumerator (SimpleDictionary sd)
{
items = new DictionaryEntry[sd.Count];
Array.Copy(sd.items, 0, items, 0, sd.Count);
}
從代碼中可以看出,它 把外部類中的所有元素拷貝到另一塊內存中進行枚舉,這樣在多線程訪問集合時 自然不會出錯,但如果集合中的元素很多就會帶來性能上的損失。而我們實現的 ReversibleSortedList類將直接使用集合中的元素進行枚舉,所以需要使用 version來保證在出錯時可以彈出異常。
下面我們在 “ReversibleSortedList 0.3版本”的基礎上繼續構建。關於 version的代碼這裡不再講解,請大家查看稍後完整的0.4版本的代碼。首先添加 一個實現枚舉器的嵌套類:
private struct Enumerator<K, V> : IEnumerator<KeyValuePair<K, V>>, IDisposable,
IDictionaryEnumerator, IEnumerator
{
private ReversibleSortedList<K, V> _ReversibleSortedList;
private K key;
private V value;
private int index;
private int version;
internal Enumerator(ReversibleSortedList<K, V> ReversibleSortedList)
{ //獲取外部類中的元素引用,以對 它進行枚舉
this._ReversibleSortedList = ReversibleSortedList;
this.index = 0;
this.version = this._ReversibleSortedList.version;//記錄外部類版本號
//設置鍵和值為其默認類型,請參考:
//http://cgbluesky.blog.163.com/blog/static/2412355820081695340822/
this.key = default(K);
this.value = default(V);
}
public void Dispose()
{
this.index = 0;
this.key = default(K);
this.value = default(V);
}
object IDictionaryEnumerator.Key
{
get
{
if ((this.index == 0) ||
(this.index == (this._ReversibleSortedList.Count + 1)))
{
throw new InvalidOperationException(
"不能進行枚舉操作.");
}
return this.key;
}
}
public bool MoveNext()
{
if (this.version != this._ReversibleSortedList.version)
{ //版本檢查
throw new InvalidOperationException("枚舉版本檢查失敗!");
}
if (this.index < this._ReversibleSortedList.Count)
{
this.key = this._ReversibleSortedList.keys[this.index];
this.value = this._ReversibleSortedList.values [this.index];
this.index++;
return true;
}
this.index = this._ReversibleSortedList.Count + 1;
this.key = default(K);
this.value = default(V);
return false;
}
DictionaryEntry IDictionaryEnumerator.Entry
{
get
{
if ((this.index == 0) ||
(this.index == (this._ReversibleSortedList.Count + 1)))
{
throw new InvalidOperationException("不能進行枚舉操作.");
}
return new DictionaryEntry(this.key, this.value);
}
}
public KeyValuePair<K, V> Current
{
get
{
return new KeyValuePair<K, V>(this.key, this.value);
}
}
object IEnumerator.Current
{
get
{
if ((this.index == 0) ||
(this.index == (this._ReversibleSortedList.Count + 1)))
{
throw new InvalidOperationException("不能進行 枚舉操作");
}
return new DictionaryEntry(this.key, this.value);
}
}
object IDictionaryEnumerator.Value
{
get
{
if ((this.index == 0) ||
(this.index == (this._ReversibleSortedList.Count + 1)))
{
throw new InvalidOperationException("不能進行 枚舉操作");
}
return this.value;
}
}
void IEnumerator.Reset()
{
if (this.version != this._ReversibleSortedList.version)
{
throw new InvalidOperationException("枚舉版本檢查失敗! ");
}
this.index = 0;
this.key = default(K);
this.value = default (V);
}
}
這個嵌套類的代碼對照圖3很 容易看懂,每個方法的功能在MSDN中也有詳細的介紹,這裡不再對它進行講解。 接下來要給外部類實現IEnumerable<KeyValuePair<TKey, TValue>>和 IEnumerable接口。更改類聲明代碼如下:
public class ReversibleSortedList<TKey, TValue> :
IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
當然,這兩個接口分別只有一個成員:GetEnumerator()方 法,剛才所創建的嵌套類就是為這個方法所創建的。接下來在 ReversibleSortedList類中使用顯式接口成員實現來實現這兩個接口:
IEnumerator<KeyValuePair<TKey, TValue>>
IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return new Enumerator<TKey, TValue>(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator<TKey, TValue>(this);
}
好,現在 更改Main()方法中的代碼看看是否可以使用foreach循環來訪問 ReversibleSortedList中的元素,當然,前面所寫的Print()成員方法可以退休 了。
static void Main()
{
ReversibleSortedList<int, string> rs=new ReversibleSortedList<int, string>();
rs.Add (3,"a");
rs.Add(1,"b");
rs.Add(2,"c");
rs.Add(6,"d");
rs.Add(5,"e");
rs.Add (4,"f");
foreach (KeyValuePair<int, string> d in rs)
{
Console.WriteLine (d.Key + " " + d.Value);
}
}
由於代碼已經達到300行,貼到博客上會導致運行緩慢,後面所 有的可運行代碼將以文件的形式給出,大家可以直接下載運行。
ReversibleSortedList 0.4版本:實現迭代
運行結果:
1 b
2 c
3 a
4 f
5 e
6 d
真棒,現在已經取得了階段性的成果。但還有一些遺憾 ,雖然在Enumerator類中實現了IDictionaryEnumerator接口,但還不能在 foreach中使用DictionaryEntry訪問元素。這是因為IDictionary接口重載了 GetEnumerator()接口,而它返回的是一個IDictionaryEnumerator接口,也就是 說,只有實現了IDictionary接口才能使用DictionaryEntry訪問其中元素。實現 IDictionary接口之前我們需要建立一些輔助的內部類,這將在下一節進行講解 。
本文配套源碼