键控集合和 IDictionary

  除 IList 接口外,集合还可以实现类似的 IDictionary 接口,允许项通过键值(如字符串)进行索引,而不是通过一个索引。这也可以使用索引符来完成,但这次的索引符参数是与存储的项相关联的键,而不是 int 索引,这样集合就更便于用户使用了。

  与索引的集合一样,可使用一个基类简化 IDictionary 接口的实现,这个基类就是 DictionaryBase,它也实现 IEnumerableICollection,提供了对任何集合都相同的基本集合处理功能。

  DictionaryBaseCollectionBase 一样,实现通过其支持的接口获得的一些成员(但不是全部成员)。DictionaryBase 也实现 ClearCount 成员,但不实现 RemoveAt()。这是因为 RemoveAt()IList 接口中的一个方法,不是 IDictionary 接口中的一个方法。但是,IDictionary 有一个 Remove() 方法,这是一个应在基于 DictionaryBase 的定制集合类上实现的方法。

  下面的代码是 Animals 类的另一个版本,这次该类派生于 DictionaryBase。下面代码包括 Add()Remove() 和一个通过键访问的索引符的实现代码:

  1. public class Animals : DictionaryBase
  2. {
  3. public void Add(string newID, Animal newAnimal)
  4. {
  5. Dictionary.Add(newID, newAnimal);
  6. }
  7. public void Remove(string animalID)
  8. {
  9. Dictionary.Remove(animalID);
  10. }
  11. public Animals() { }
  12. public Animal this[string animalID]
  13. {
  14. get {
  15. return (Animal)Dictionary[animalID];
  16. }
  17. set {
  18. Dictionary[animalID] = value;
  19. }
  20. }
  21. }

  这些成员的区别如下:

  ● Add() — 带有两个参数:一个键和一个值,存储在一起。字典集合有一个继承于 DictionaryBase 的成员 Dictionary,这个成员是一个 IDictionary 接口,有自己的 Add() 方法,该方法带有两个 object 参数。我们的实现代码使用一个 string 值作为键,使用一个 Animal 对象作为与该键存储在一起的数据。

  ● Remove() — 以一个键而不是对象引用作为参数。与指定键值对应的项被删除。

  ● Indexer — 使用一个字符串键值,而不是一个索引,用于通过 Dictionary 的继承成员来访问存储的项,这里仍需进行数据类型转换。

  基于 DictionaryBase 的集合和基于 CollectionBase 的集合之间的另一个区别是 foreach 的工作方式稍有区别。上一节的集合可以直接从集合中提取 Animal 对象。使用 foreachDictionaryBase 派生类可以提供 DictionaryEntry 结构,这是另一个在 System.Collections 名称空间中定义的类型。要得到 Animal 对象本身,就必须使用这个结构的 Value 成员,也可以使用结构的 Key 成员得到相关的键。要使代码等价于前面的代码:

  1. foreach (Animal myAnimal in animalCollection)
  2. {
  3. Console.WriteLine("New {0} object added to custom collection, " +
  4. "Name = {1}", myAnimal.ToString(), myAnimal.Name);
  5. }

  需要使用以下代码:

  1. foreach (DictionaryEntry myEntry in animalCollection)
  2. {
  3. Console.WriteLine("New {0} object added to custom collection, " +
  4. "Name = {1}", myEntry.Value.ToString(),
  5. ((Animal)myEntry.Value).Name);
  6. }

  有许多方式可以重写这段代码,以便直接通过 foreach 访问 Animal 对象,最简单的方式是实现一个迭代器。