为了创建此示例,打开一个新工程并插入两个类模块。在窗体上画出五个命令按钮、一个列表框、两个文本框,以及两个标签,如图 9.13 所示。
图 9.13 Employees 集合示例

下表列出了该示例所需设置的属性值。
| 对象 | 属性 | 设置值 |
| 类模块 | Name | Employee |
| 类模块 | Name | SmallBusiness |
| 窗体 | Caption | 雇员集合 |
| 第一个命令按钮 | Caption Name |
添加 cmdAddEmployee |
| 第二个命令按钮 | Caption Name |
删除 cmdDeleteEmployee |
| 第三个命令按钮 | Caption Name |
刷新表 cmdListEmployees |
| 第四个命令按钮 | Caption Name |
有问题 cmdTrouble |
| 第五个命令按钮 | Caption Name |
关闭 cmdClose |
| 第一个标签控件 | Caption | Name |
| 第二个标签控件 | Caption | Salary |
| 第一个文本框 | Name Text |
txtName (blank) |
| 第二个文本框 | Name Text |
txtSalary (blank) |
| 列表框 | Name | lstEmployees |
在 Employee 类模块中,将下面的声明和属性过程添加进去:
Option Explicit 'Employee类的属性。Public Name As StringPublic Salary As Long'只写一次ID属性的私有数据。Private mstrID As StringProperty Get ID() As StringID = mstrIDEnd Property'首次设置ID属性时,静态的Boolean变量也要设置。'后续的调用什么也不做。'(若不是这样,产生一个错误会更好。)Property Let ID(strNew As String)Static blnAlreadySet As BooleanIf Not blnAlreadySet ThenblnAlreadySet = TruemstrID = strNewEnd IfEnd Property
ID 属性是从集合中检索或者删除 Employee 对象的键,因此必须对它进行一次性设置并不再更改。这是用 Static Boolean 变量来完成的,该变量在首次设置 ID 属性的时候被设置为 True。该属性总是可读的,因为有一个 Property Get。
在 SmallBusiness 类模块中,将下面的声明添加进去。当代码中引用 Employees 变量时,将创建集合对象。
Option Explicit
Public Employees As New Collection
所有的剩余代码都在窗体模块中。在声明部分添加下面的声明。
Option Explicit
Public sbMain As New SmallBusiness
cmdEmployeeAdd_Click 事件中的代码,将一个成员添加到集合。
Private Sub cmdEmployeeAdd_Click() Dim empNew As New Employee Static intEmpNum As Integer '用With语句,使代码更快,也更简洁'(.ID对应empNew.ID)。With empNew'为新的雇员产生一个唯一的ID。intEmpNum = intEmpNum + 1.ID = "E" & Format$(intEmpNum, "00000").Name = txtName.Text.Salary = CDbl(txtSalary.Text)'将Employee对象引用添加到集合中,'用ID属性作为键。sbMain.Employees.Add empNew, .IDEnd WithtxtName.Text = ""txtSalary.Text = ""'单击“刷新表”按钮。cmdListEmployees.Value = TrueEnd Sub
在 cmdListEmployees_Click 事件过程中的代码,用一个 For Each ... Next 语句将所有的雇员信息添加到 Listbox 控件中。
Private Sub cmdListEmployees_Click()
Dim emp As Employee
lstEmployees.Clear
For Each emp In sbMain.Employees
lstEmployees.AddItem emp.ID & ", " & emp.Name _
& ", " & emp.Salary
Next
End Sub
cmdEmployeeDelete_Click 事件用 Collection 对象的 Remove 方法,删除当前 ListBox 控件中选定的集合对象。
Private Sub cmdEmployeeDelete_Click() '检查一下,以确保有被选中的雇员。If lstEmployees.ListIndex > -1 Then'前六个字符是ID。sbMain.Employees.Remove _Left(lstEmployees.Text, 6)End If'单击“刷新表”按钮。cmdListEmployees.Value = TrueEnd Sub
将下面的代码添加给“有问题”按钮。
Private Sub cmdTrouble_Click() '说些什么!?sbMain.Employees.Add MeEnd Sub
cmdClose_Click 事件关闭该应用程序。当关闭使用对象的工程时,卸载所有窗体,确保类模块中的所有 Terminate 事件过程都将得到执行。相反,使用 End 语句终止程序显得生硬,不会执行 Terminate 事件。
Private Sub cmdClose_Click()
Unload Me
End Sub
在示例中,为了添加雇员,可以运行应用程序,在两个文本框中输入值,然后选取“添加”按钮。添加几个雇员,然后用删除和列表按钮来作一下实验。
这种简单的实现方法不是很强健。因为 Employees 属性仅仅是一个公有的 Collection 对象,可能无意中在程序中的任何地方访问它。而且,Collection 对象的 Add 方法不作任何类型检查。例如,在“有问题”按钮的 Click 事件中的代码,会轻率地将对窗体的对象引用插入到雇员集合中。
单击“有问题”按钮,会注意到未出现任何错误。现在,单击“刷新表”按钮。当 For Each ... Next 循环遇到意外的对象类型时,它将导致类型为 13 的错误发生,即“类型不匹配”。
这是用公用的 Collection 对象建立对象模型时,可能会碰到的错误的一个示例。可以从工程的任何地方添加对象,而且对于它们是否将被正确地初始化,没有任何保证。如果程序员复制代码以添加雇员,而且原始代码后来又有所改动,那么要找到产生的错误非常困难。
详细信息 本主题中开始探讨的这个示例,在下面的“私有集合示例:树枝盖的房子”中将继续讨论。