我们前面已经讲过 VBA 的对象,包括 VBA 内部对象如 Collection,以及外部对象如 Scripting.Dictionary 等,现在我们来讨论用户如何创造自己的对象。我们前面所有《Excel VBA 编程基础》中的代码都是写在代码模块中的。VBA 有若干种模块类:- 代码模块(Code Module):现在称为标准模块(Standard Module),简称模块(Module),我们在工程资源管理器中看到的就是这个简称。
- 类模块(Class Module):用户所写的类(Classes)都放在这个模块类中。
- 窗体模块(User Form):这个类中包括了用户构造的窗体,譬如用来录入数据的窗体等。
以上这些都是 VBA 中模块的分类,每个模块类中都可以包含多个模块,譬如代码模块类中可以包含多个代码模块(模块1,模块2,等),每个代码模块都可以包含多个类型定义、变量声明、Sub/Function 过程等。类模块类中可以会包含多个类模块(Class1,Class2,等)每个类模块对应一个类。窗体模块类中可以包含多个窗体模块(Form1,Form2,等),每个窗体模块对应一个窗体。窗体模块以后再说,代码模块我们已经很熟悉了,现在主要讨论类模块。关于模块与类模块的详细信息,请参阅相关阅读。类实际上是一种类型定义,而对象是这个类型定义的具体实例。优点类似于基本数据类型中的类型与具体数值的关系。例如:在这个代码片段中,Integer 是一个类型,32 则是该类型的一个具体实例,n 则是 Integer 类型的一个变量,并且它的值是 32。这样,类型 Integer 和 Integer 的一个具体实例通过变量 n 联系了起来。Dim o As CollectionSet o =New Collection
在这个片段中,Collection 是一个类型,o 是 Collection 类型的一个变量,New Collection 创建 Collection 类型的一个实例(对象),Set 语句将 Collection 实例的地址赋给对象变量 o。New Collection 创建的实例我们看不见,只有通过对象变量 o 才能访问这个实例。基本类型的实例(譬如 32)是简单的、无结构的,只能作为整体参与运算。类的实例则不同,是有结构的,复杂的,不仅具有状态,还具有方法,用户通过实例的方法来操纵实例(对象)的状态,而且还能够引发事件。所以,类需要通过特殊的途径来进行实例化以创造新的对象。Dim o As CollectionSet o =New Collectiono.Add "Apple"o.Add "Watermelon"o.Add "Grape"Debug.Print o.Count
类 Collection 定义了 Add 方法,所以我们可以通过对象变量 o 来调用 Add 方法,以向 Collection 的实例中增加内容,也就是改变该实例的状态。最后,打印输出该实例所包含的元素个数。注意:经过这一系列的 Add 操作之后,这个实例仍然是同一个实例,虽然其内部状态已经发生了变化(包含三个元素)。这是类与对象的一个非常重要的的特点:不管实例的内部状态如何变化,只要还有对象变量指向该实例,该实例就一直存在,并且仍然是同一个实例。就像人一样,不管体重如何变化,身高如何变化,人还是同一个人。Dim o As CollectionSet o =New Collectiono.Add Key:="Name", Item:="Jobs"Set o =New Collectiono.Add Key:="Name", Item:="Gates"Debug.Print o("Name")
在这个例子中,我们先是在第3行创造了一个对象(Collection 的实例),然后向该对象中增加了一个条目 Jobs。然后在第6行又创造了一个对象并且赋给对象变量 o。此时,o 原来指向的对象(即含有条目 Jobs 的对象),因为没有指针指向它,因此被 VBA 的运行时系统回收,此时我们就说这个对象消失了、销毁了。这种回收对象的行为称为垃圾回收(garbage collection)。第6行结束后,o 指向一个新的对象,然后第7行向该对象中添加条目 Gates。最后的输出结果就是这个新对象的条目。从这个例子中,我们看到:类可以多次实例化,从而创造多个对象。一旦对象没有指针指向它,则被 VBA 的运行时系统回收。从而可以避免在 C/C++ 程序中经常发生的内存泄漏(memory leaks)。从这个例子中可以看出,一个对象可以有多个对象变量指向它,只要有一个对象变量指向它,它就会一直存在。而且,就像例子所展示的,对象变量 o 和 o2 都指向同一个对象,则通过 o 对对象的操作和通过 o2 对对象的操作,其作用是相同的。这个例子也向我们展示了对象变量赋值的特殊之处。对于基本类型的变量,其赋值是拷贝赋值:Dim n As IntegerDim m As Integern = 32m = n
m = n 的赋值是将 n 当前的值拷贝给 m,赋值以后,n 和 m 各自保有自己的值。Dim o As CollectionDim o2 As CollectionSet o =New CollectionSet o2 = o
Set o2 = o 的赋值是将 o 的内容(o 所指向的对象的地址)赋给 o2,而不是将 o 所指向的内容(Collection 实例)拷贝给 o2。经过 Set o2 = o 的赋值之后,o 和 o2 指向同一个对象。此时,通过 o 对对象的操作,等同于通过 o2 对对象的操作。