强类型集合基类
在System.Collections命名空间中,又提供了3个抽象的基类,它们分别是强类型集合抽象基类CollectionBase、键/值强类型集合抽象基类DictionaryBase、强类型只读集合抽象基类ReadOnlyCollectionBase。这些基类主要是为我们提供创建自定义集合的机会,它们都提供了创建强类型集合的功能,推荐使用它们对这些类进行扩展,而不是自己去编写自己的基类。因为自己编写基类需要创建一个接口ICollection,就要先实现IEnumerable接口,自己去实现这个接口还是比较不方便的。
下面通过介绍DictionaryBase基类的使用来作为代表,它们三个的实现原理基本相同,使用方式也基本一样。图10-15是DictionaryBase基类的定义:
图10-15
所以DictionaryBase基类可以通过简单的迭代来遍历,而不能通过下标的方式来遍历,因为它并没有实现Ilist接口。那么使用它派生的新类,除非实现了其他新的接口,那么新类也只能够通过简单的迭代方式来遍历。
由于DictionaryBase的每个元素都是一个键/值对,因此元素类型既不是键的类型,也不是值的类型。而是DictionaryEntry类型。
图10-16列出了DictionaryBase基类的属性。
图10-16
在DictionaryBase中已经实现了一个DictionaryEntry类型的列表,可以通过属性Dictionary或者InnerHashtable来获得这个列表。
图10-17列出了DictionaryBase中已经显式的接口实现,所以可以直接使用这些功能来扩充自己定义的类。
图10-17
在DictionaryBase中有一些比较特殊的保护方法,这些方法都是以On开头,这些方法的功能是在执行某个操作之前或者之后能够被自动调用的方法,在这些方法中可以添加一些代码,以达到对数据操作的验证或者纠错功能。比如,方法OnInsert就是在向DictionaryBase实例中插入新元素之前被自动执行。而方法OnInsertComplete在向DictionaryBase实例中插入新元素之后被执行。一般不使用DictionaryBase中InnerHashtable来获得列表,就是因为使用InnerHashtable获得的列表在执行增加,插入等操作的同时,不能够触发这些以On开头的方法。
例子DictionaryBaseSample中,创建了自定义类MyDictionary。在新类中实现了长度小于或等8个字符的String类型的键以及长度不超过11个长度的String类型的值。使用这个类生成了一个存储姓名(键)/电话号码(值)的实例。
自定义类MyDictionary的代码
using System;
using System.Collections;//注意命名空间
using System.Linq;
using System.Text;
namespace DictionaryBaseSample
{
class MyDictionary:DictionaryBase
{
/// <summary>
/// 实现通过Key获得与设置值的功能
/// </summary>
/// <param name="key">String类型</param>
/// <returns>如果是get方法,将或者值,set方法不返回</returns>
public String this[String key]
{
get
{
return ((String)Dictionary[key]);
}
set
{
Dictionary[key] = value;
}
}
/// <summary>
/// 实现获得字典中键的集合
/// </summary>
public ICollection Keys
{
get
{
return (Dictionary.Keys);
}
}
/// <summary>
/// 实现获得字典中值的集合
/// </summary>
public ICollection Values
{
get
{
return (Dictionary.Values);
}
}
/// <summary>
/// 实现向字典中添加新项的操作
/// </summary>
/// <param name="key">键:String类型</param>
/// <param name="value">值:String类型</param>
public void Add(String key, String value)
{
Dictionary.Add(key, value);
}
/// <summary>
/// 判断具有key键的项是否在字典中
/// </summary>
/// <param name="key">键:String类型</param>
/// <returns>如果包含,返回true,否则返回false</returns>
public bool Contains(String key)
{
return (Dictionary.Contains(key));
}
/// <summary>
/// 从字典中移除键是key的项
/// </summary>
/// <param name="key">键:String类型</param>
public void RemoveByKey(String key)
{
Dictionary.Remove(key);
}
/// <summary>
/// 从字典移除值是value的项
/// </summary>
/// <param name="Value">值:String类型</param>
public void RemoveByValue(String Value)
{
ArrayList al=new ArrayList();
//遍历字典,找到所有值是Value的键的集合
foreach (DictionaryEntry de in Dictionary)
{
if (String.Equals(de.Value, Value))
al.Add(de.Key);
}
//从字典中将这些键的项全部删除
foreach (String str in al)
{
RemoveByKey(str);
}
}
/// <summary>
/// 在插入项前,调用进行有效性验证
/// </summary>
/// <param name="key">键:String类型</param>
/// <param name="value">值:String类型</param>
protected override void OnInsert(Object key, Object value)
{
if (key.GetType() != typeof(System.String))
throw new ArgumentException("键不是String类型", "key");
else
{
String strKey = (String)key;
if (strKey.Length > 5)
throw new ArgumentException("键的长度不能超过5", "key");
}
if (value.GetType() != typeof(System.String))
throw new ArgumentException("值不是String类型", "value");
else
{
String strValue = (String)value;
if (strValue.Length > 11)
throw new ArgumentException("值的长度不能超过11", "value");
}
}
/// <summary>
///在设置值时之前,调用进行有效性验证
/// </summary>
/// <param name="key">键:String类型</param>
/// <param name="oldValue">旧值:String类型</param>
/// <param name="newValue">新值:String类型</param>
protected override void OnSet(Object key, Object oldValue, Object newValue)
{
if (key.GetType() != typeof(System.String))
throw new ArgumentException("键必须是String类型.", "key");
else
{
String strKey = (String)key;
if (strKey.Length > 5)
throw new ArgumentException("键长度不能超过5.", "key");
}
if (newValue.GetType() != typeof(System.String))
throw new ArgumentException("值必须是String类型.", "newValue");
else
{
String strValue = (String)newValue;
if (strValue.Length > 11)
throw new ArgumentException("值长度不能超过11.", "newValue");
}
}
/// <summary>
/// 在删除指定键的项时,对输入的键进行验证
/// </summary>
/// <param name="key">键:String类型</param>
/// <param name="value">值:String类型</param>
protected override void OnRemove(Object key, Object value)
{
if (key.GetType() != typeof(System.String))
throw new ArgumentException("键不是String类型.", "key");
else
{
String strKey = (String)key;
if (strKey.Length > 5)
throw new ArgumentException("键长度不能超过5.", "key");
}
}
/// <summary>
/// 验证带有指定键和值的元素时执行
/// </summary>
/// <param name="key">键:String类型</param>
/// <param name="value">值:String类型</param>
protected override void OnValidate(Object key, Object value)
{
if (key.GetType() != typeof(System.String))
throw new ArgumentException("键不是String类型.", "key");
else
{
String strKey = (String)key;
if (strKey.Length > 5)
throw new ArgumentException("键长度不能超过5.", "key");
}
if (value.GetType() != typeof(System.String))
throw new ArgumentException("值必须是String类型.", "value");
else
{
String strValue = (String)value;
if (strValue.Length > 11)
throw new ArgumentException("值长度不能超过11.", "value");
}
}
}
}
用来测试的代码
using System;
using System.Collections;//注意命名空间
using System.Linq;
using System.Text;
namespace DictionaryBaseSample
{
class Program
{
static void Main(string[] args)
{
//建立一个自定义的字典实例
MyDictionary mydict = new MyDictionary();
mydict.Add("武松", "135********");
mydict.Add("林冲", "136********");
mydict.Add("鲁智深", "137********");
mydict.Add("吴用", "135********");
mydict.Add("宋江", "139********");
Console.WriteLine("在foreach语句中使用DictionaryEntry打印:");
PrintKeysAndValues1(mydict);
Console.WriteLine("使用枚举数打印字典的内容:");
PrintKeysAndValues2(mydict);
Console.WriteLine("在foreach语句中使用键的具体类型打印:");
PrintKeysAndValues3(mydict);
//试图插入一个值的长度超过11的项
try
{
mydict.Add("李逵", "123456789012");
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message.ToString());
}
//试图插入一个键的长度超过5的项
try
{
mydict.Add("ABCDEFG", "123456789");
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message.ToString());
}
Console.WriteLine();
// 判断某个键是否在字典中
Console.WriteLine("字典中{0}/"林冲/"的数据项。", mydict.Contains("林冲")?" 有 ":"没有");
Console.WriteLine("字典中{0}/"孙二娘/"的数据项。", mydict.Contains("孙二娘")?" 有 ":"没有");
Console.WriteLine();
//删除指定键的项
mydict.RemoveByKey("宋江");
//打印字典中全部的内容
Console.WriteLine("删除/"宋江/"后:");
PrintKeysAndValues1(mydict);
//删除所有值是135********的项
Console.WriteLine("删除 /"135********/"后:");
mydict.RemoveByValue("135********");
PrintKeysAndValues1(mydict);
Console.ReadKey();
}
//遍历字典的方法一
public static void PrintKeysAndValues1(MyDictionary myCol)
{
foreach (DictionaryEntry myDE in myCol)
Console.WriteLine(" {0,-5} : {1}", myDE.Key, myDE.Value);
Console.WriteLine();
}
//遍历字典的方法二
public static void PrintKeysAndValues2(MyDictionary myCol)
{
DictionaryEntry myDE;
System.Collections.IEnumerator myEnumerator = myCol.GetEnumerator();
while (myEnumerator.MoveNext())
if (myEnumerator.Current != null)
{
myDE = (DictionaryEntry)myEnumerator.Current;
Console.WriteLine(" {0,-5} : {1}", myDE.Key, myDE.Value);
}
Console.WriteLine();
}
//遍历字典的方法三
public static void PrintKeysAndValues3(MyDictionary myCol)
{
ICollection myKeys = myCol.Keys;
foreach (String k in myKeys)
Console.WriteLine(" {0,-5} : {1}", k, myCol[k]);
Console.WriteLine();
}
}
}
程序的运行结果
在foreach语句中使用DictionaryEntry打印:
鲁智深 : 137********
宋江 : 139********
林冲 : 136********
武松 : 135********
吴用 : 135********
使用枚举数打印字典的内容:
鲁智深 : 137********
宋江 : 139********
林冲 : 136********
武松 : 135********
吴用 : 135********
在foreach语句中使用键的具体类型打印
鲁智深 : 137********
宋江 : 139********
林冲 : 136********
武松 : 135********
吴用 : 135********
值长度不能超过11.
参数名: value
键长度不能超过5.
参数名: key
字典中 有 "林冲"的数据项。
字典中没有"孙二娘"的数据项。
删除"宋江"后:
鲁智深 : 137********
林冲 : 136********
武松 : 135********
吴用 : 135********
删除 "135********"后:
鲁智深 : 137********
林冲 : 136********
System.Collections.Specialized命名空间与System.Collections命名空间在功能级别上看,它们的作用都是提供了一系列的集合的类。System.Collections命名空间提供的类操作数据的级别停留在Object上,因为Object是所有类的基类,所以这些类具有更大的通用性。而System.Collections.Specialized命名空间提供的类包含专用的和强类型的集合。例如,链接的列表词典、位向量以及只包含字符串的集合等。这些类的使用具有比较多的要求,或者应用的领域很窄。所以,本书在这里不做过多的介绍。