19.3 自定义集合

如果需要一个特定的集合类型,BCL中提供的集合类型无法满足需要时,例如要求一种集合类型只能添加某种数据类型到集合中,而不允许添加其他的类型;或者需要在元素添加或删除时进行某种额外的验证等,此时就需要自己来定义一个新的集合类型。不要把这件事想象的很难,事实上要自定义一个集合类型非常容易,在BCL中有抽象集合基类,我们只需要从这些基类继承,并添加一些定制的特性,即可设计出自定义的集合类型。

BCL中可供使用的抽象集合基类有两个。

❑CollectionBase:从该基类可以派生出集合元素可修改的强类型自定义集合;

❑ReadOnlyCollectionBase:此基类为CollectionBase的只读版本,可由它派生出非泛型的自定义集合,且该集合是只读的。

关于这两个抽象基类的用法,我们将在稍后分别介绍。

19.3.1 CollectionBase

继承并扩展CollectionBase可以创建一个可读写的集合类型。在开始之前,先对该类进行简单的介绍。CollectionBase类中提供了两个只读属性:InnerList和List,前者是ArrayList类型,用于存放集合的数据元素;后者是返回自定义集合类型的实例自身的引用,如下所示:


protected ArrayList InnerList

{

get

{

if(this.list==null)

{

this.list=new ArrayList();

}

return this.list;

}

}

protected IList List

{

get

{

return this;

}

}


注意这两个属性的访问修饰符:protected,意味着只有它自己和它的派生类能够访问。

应使用List来获取基类中的集合,而不是直接使用InnerList,原因是CollectionBase类提供了一些集合操作的方法,例如Add、Insert、Remove等,这些方法内部操作的就是InnerList集合,只不过在操作InnerList集合的前后都有一些事件方法可用。这里以Add方法为例,源代码如下:


//Add方法

int IList.Add(object value)

{

this.OnValidate(value);

this.OnInsert(this.InnerList.Count,value);

int index=this.InnerList.Add(value);

try

{

this.OnInsertComplete(index,value);

}

catch

{

this.InnerList.RemoveAt(index);

throw;

}

return index;

}


从上述代码可以看到,在进行真正的集合(InnerList)操作的前、后都有一些事件方法,例如OnValidate、OnClear、OnInsert、OnInsertComplete等。这些事件方法是虚方法,我们可以在自定义集合中根据需要重写它们,可以在这些事件方法中执行一些前置检查,或者结果检查。需要注意的是,尽量不要自己调用这些事件方法,除非你明确地想触发这些事件。这里以OnValidate方法为例,源码如下:


protected virtual void OnValidate(object value)

{

if(value==null)

{

throw new ArgumentNullException("value");

}

}


可见,此事件方法可以在数据加入集合前执行一些验证的操作。基类提供了丰富的事件方法,表19-11中列出了所有这些事件方法,并包括说明。

19.3 自定义集合 - 图1

现在给出一个较完整的示例,在代码清单19-14中,定义一个名为Students的集合,它派生自CollectionBase基类。该集合提供了Add、Insert、IndexOf、Remove等方法,并且重写了几个事件方法。

代码清单19-14 从CollectionBase派生自定义集合代码示例


using System;

using System.Collections;

namespace ProgrammingCSharp4

{

class CollectionSample

{

public static void Main()

{

try

{

Students students=new Students();

students.Add(new Student{Name="Tom",Sex="Male"});

students.Add(new Student{Name="Alice",Sex="Female"});

students.Add(new Student{Name="Jack",Sex="Male"});

PrintCollectionInfo(students,"students");

students.Insert(1,new Student{Name="Bill",Sex="Male"});

PrintCollectionInfo(students,"students");

students.RemoveAt(2);

PrintCollectionInfo(students,"students");

students.Add("other");

}

catch(Exception e)

{

Console.WriteLine(“异常:”);

Console.WriteLine("-".PadLeft(50,'-'));

Console.WriteLine(e.Message);

}

}

public class Students:CollectionBase

{

public Students()

:base()

{

}

public void Add(Object student)

{

List.Add(student);

}

public void Insert(int index,Object student)

{

List.Insert(index,student);

}

public void Remove(Object student)

{

List.Remove(student);

}

public int IndexOf(Object student)

{

return List.IndexOf(student);

}

public bool Contains(Object student)

{

return List.Contains(student);

}

public Object this[int index]

{

get

{

return List[index];

}

}

protected override void OnInsert(int index,Object student)

{

Console.WriteLine(“在索引{0}插入了{1}”,index,(student as Student).Name);

}

protected override void OnRemove(int index,Object student)

{

Console.WriteLine(“在索引{0}移除了{1}”,index,(student as Student).Name);

}

protected override void OnSet(int index,Object oldValue,Object newValue)

{

Console.WriteLine(“在索引{0}将值{1}修改为{2}”,index,

(oldValue as Student).Name,(newValue as Student).Name);

}

protected override void OnValidate(Object student)

{

if(student.GetType()!=typeof(Student))

throw new ArgumentException(“集合的元素类型只能为Student类型.”,"student");

}

}

public class Student

{

public string Name{get;set;}

public string Sex{get;set;}

public string Grade{get;set;}

public DateTime Birthday{get;set;}

}

private static void PrintCollectionInfo(Students stuList,string varName)

{

Console.Write("{0}:",varName);

foreach(Student student in stuList)

{

Console.Write("\t{0}",student.Name);

}

Console.WriteLine();

Console.WriteLine("Count:{0}",stuList.Count);

}

}

}


上述代码的运行结果为:


在索引0插入了Tom

在索引1插入了Alice

在索引2插入了Jack

students:Tom Alice Jack

Count:3

在索引1插入了Bill

students:Tom Bill Alice Jack

Count:4

在索引2移除了Alice

students:Tom Bill Jack

Count:3

异常:


集合的元素类型只能为Student类型.

参数名:student

请按任意键继续……