C#对集合类型有统一的规范。它的好处不言而喻,所有集合类都有一些统一的调用方法和属性,这使得学习成本大大降低。统一的规范就是通过接口来实现的(关于接口,如果不熟,请参考
http://www.enet.com.cn/eschool/video/c/30.shtml ),另一方面一些类也会直接调用这些标准接口,使得我们写出来的类有更好的兼容性。最典型的例子莫过于IEnumerable接口,只要实现了它就可以使用foreach语句进行调用。
我们将要给ReversibleSortedList实现的是IDictionary接口,先来看看它的定义:
public interface IDictionary : ICollection, IEnumerable
ICollection接口是所有集合类的基接口,FCL中所有集合,不管是哪种方式的集合都实现它。IEnumerable接口则是枚举器接口,实现了它就可以使用foreach语句对它进行访问。IDictionary接口则继承自这两个接口,它表示键/值对的非通用集合。
ICollection接口的定义为:
public interface ICollection : IEnumerable
从这个定义可以看出,所有集合类都应该支持foreach语句进行访问。
表1列出了各个接口的成员
从上表可以看出,实现IDictionary接口并不简单。我个人喜欢先把复杂的问题简单化,但所要实现的东西实在太多,只能尽力而为。如果在我们之前所构建的ReversibleSortedList类中加上这么代码将不利于理解如何实现IDictionary接口。所以我从MSDN上copy了一段IDictionary接口实现的代码,并把所有属于出错判断的部分咔嚓掉,先让大家对IDictionary接口有个了解后再给ReversibleSortedList类实现它。
实现这个接口需要注意以下几点:
1. IEnumerable接口的GetEnumerator方法的实现,这个方法的原型为:
IEnumerator GetEnumerator()
也就是说这个方法返回的是一个实现了IEnumerator的类,IEnumerator接口的成员如下:
Current属性:获取集合中的当前元素
MoveNext方法:将枚举数推进到集合的下一个元素
Reset方法:将枚举数设置为其初始位置,该位置位于集合中第一个元素之前
实现了IEnumerator接口的类可以通过一个内部类来实现,又要多实现三个成员,事态变得进一步复杂。关于IEnumerable接口实现,如果不懂可以上网搜搜,能搜一大箩筐出来,也可以参考设计模式中的“Iterator 迭代器模式”进行学习。
IDictionary接口中有自己的GetEnumerator方法,它返回的IEnumerator接口的子接口:IDictionaryEnumerator接口。这个接口的定义为:
public interface IDictionaryEnumerator : IEnumerator
它的成员为:
Entry:同时获取当前字典项的键和值
Key:获取当前字典项的键
Value:获取当前字典项的值
这意味着除了要实现IEnumerable接口的三个成员外,还要实现它自己的三个成员,变得更复杂了,晕!值得庆幸的是由于IDictionaryEnumerator接口继承自IEnumerator接口,可以把IDictionaryEnumerator接口强制转换为IEnumerator接口用于实现IEnumerable接口的GetEnumerator方法。当然,由于两个方法同名,只能给IEnumerable接口用显示接口成员实现了。
2. Item属性,这个属性并不是叫你实现了个名字叫“Item”的属性,而是一个索引器,通过实例名加方括号中的索引来访问集合里的元素。关于索引器,如果不熟,请参考:
http://www.enet.com.cn/eschool/video/c/20.shtml 。
3. Keys属性,这个属性的原型为:
ICollection Keys { get; }
也就是说,它返回一个实现了ICollection接口的类。ICollection接口前面已经讲过,C#中的所有集合类都实现了它。本例中这个属性返回的是一个数组,因为数组也实现了ICollection接口。另外Values属性也是同样的情况。
4. 本例中,字典集合里的一个元素是由键和值组成的,这一个元素使用了FCL中现成的DictionaryEntry来实现,它定义了可设置或检索的字典键/值对。
下面列出了代码,请大家参照注释进行理解。
IDictionary接口的实现(以下代码可直接拷贝并运行)
using
System;
using
System.Collections;
public
class
SimpleDictionary : IDictionary
{
private
DictionaryEntry[] items;
//
用数组存放元素
private
Int32 ItemsInUse
=
0
;
//
元素个数
//
指定存储空间的构造方法
public
SimpleDictionary(Int32 numItems)
{
items
=
new
DictionaryEntry[numItems];
}
#region
IDictionary 成员
public
bool
IsReadOnly {
get
{
return
false
; } }
public
bool
Contains(
object
key)
{
//
检测是否包含指定的key键
Int32 index;
return
TryGetIndexOfKey(key,
out
index);
}
public
bool
IsFixedSize {
get
{
return
false
; } }
public
void
Remove(
object
key)
{
//
移除指定键的元素
Int32 index;
if
(TryGetIndexOfKey(key,
out
index))
{
//
把移除元素后面的所有元素向前移动一个位置
Array.Copy(items, index
+
1
, items, index, ItemsInUse
-
index
-
1
);
ItemsInUse
--
;
}
}
public
void
Clear() { ItemsInUse
=
0
; }
//
清除所有元素
public
void
Add(
object
key,
object
value)
{
//
添加一个元素
items[ItemsInUse
++
]
=
new
DictionaryEntry(key, value);
}
public
ICollection Keys
{
//
返回所有键的集合
get
{
//
把所有键的集合拷贝到新数组中并返回
Object[] keys
=
new
Object[ItemsInUse];
for
(Int32 n
=
0
; n
<
ItemsInUse; n
++
)
keys[n]
=
items[n].Key;
return
keys;
}
}
public
ICollection Values
{
//
返回所有值的集合
get
{
//
把所有值的集合拷贝到新数组中并返回
Object[] values
=
new
Object[ItemsInUse];
for
(Int32 n
=
0
; n
<
ItemsInUse; n
++
)
values[n]
=
items[n].Value;
return
values;
}
}
public
object
this
[
object
key]
{
//
item属性的实现,也就是索引器
get
{
Int32 index;
if
(TryGetIndexOfKey(key,
out
index))
{
return
items[index].Value;
}
return
null
;
}
set
{
Int32 index;
if
(TryGetIndexOfKey(key,
out
index))
{
items[index].Value
=
value;
}
else
{
Add(key, value);
}
}
}
//
这个方法有两个作用,第一是查找指定键是否存在
//
第二是返回指定键的索引
private
Boolean TryGetIndexOfKey(Object key,
out
Int32 index)
{
for
(index
=
0
; index
<
ItemsInUse; index
++
)
{
if
(items[index].Key.Equals(key))
return
true
;
}
return
false
;
}
//
用于迭代的嵌套类,它同时实现了IEnumerator和IDictionaryEnumerator
private
class
SimpleDictionaryEnumerator : IDictionaryEnumerator
{
DictionaryEntry[] items;
Int32 index
=
-
1
;
//
构造方法,用于获得SimpleDictionary类的所有元素
public
SimpleDictionaryEnumerator(SimpleDictionary sd)
{
items
=
new
DictionaryEntry[sd.Count];
Array.Copy(sd.items,
0
, items,
0
, sd.Count);
}
public
Object Current
{
get
{
return
items[index]; }
}
public
DictionaryEntry Entry
{
get
{
return
(DictionaryEntry) Current; }
}
public
Object Key {
get
{
return
items[index].Key; } }
public
Object Value {
get
{
return
items[index].Value; } }
public
Boolean MoveNext()
{
if
(index
<
items.Length
-
1
) { index
++
;
return
true
; }
return
false
;
}
public
void
Reset()
{
index
=
-
1
;
}
}
//
实现IDictionary接口中的GetEnumerator方法
public
IDictionaryEnumerator GetEnumerator()
{
return
new
SimpleDictionaryEnumerator(
this
);
}
#endregion
#region
ICollection 成员
public
bool
IsSynchronized {
get
{
return
false
; } }
//
这句够简化,直接弹出异常不给使用
public
object
SyncRoot {
get
{
throw
new
NotImplementedException(); } }
public
int
Count {
get
{
return
ItemsInUse; } }
public
void
CopyTo(Array array,
int
index) {
throw
new
NotImplementedException(); }
#endregion
#region
IEnumerable 成员
//
实现IEnumerable接口的GetEnumerator方法
IEnumerator IEnumerable.GetEnumerator()
{
//
这里使用了强制类型转换.
return
((IDictionary)
this
).GetEnumerator();
}
#endregion
}
public
sealed
class
App
{
static
void
Main()
{
//
创建一个只能包含三个元素的字典类
IDictionary d
=
new
SimpleDictionary(
3
);
//
添加三个人名和它们的年龄到字典内
d.Add(
"
Jeff
"
,
40
);
d.Add(
"
Kristin
"
,
34
);
d.Add(
"
Aidan
"
,
1
);
Console.WriteLine(
"
字典元素个数= {0}
"
, d.Count);
Console.WriteLine(
"
字典中是否包含'Jeff'? {0}
"
, d.Contains(
"
Jeff
"
));
Console.WriteLine(
"
Jeff的年龄是:{0}
"
, d[
"
Jeff
"
]);
//
显示字典中的所有键和值,由于实现了IDictionaryEnumerator接口
//
所以可以使用foreach进行调用
foreach
(DictionaryEntry de
in
d)
{
Console.WriteLine(
"
{0} is {1} years old.
"
, de.Key, de.Value);
}
//
移除“Jeff”
d.Remove(
"
Jeff
"
);
//
移除不存在的元素不会引发异常
d.Remove(
"
Max
"
);
//
显示字典中的所有人名(key)
foreach
(String s
in
d.Keys)
Console.WriteLine(s);
//
显示字典中的所有年龄(value)
foreach
(Int32 age
in
d.Values)
Console.WriteLine(age);
}
}