上文中我们说过,一个对象的属性(property)是对象与外界沟通从而变更自己内部状态的中转机构。有了属性这个中转机构,我们对对象的内部状态就可以有更多的控制。譬如说,我们可以有:在 NotSimpleClass 中,我们增加了一个只读属性 Id,这个属性不需要用户设置,是类实例化时内部生成的,每一个 NotSimpleClass 的实例都有自己唯一的 Id。从代码可以看出,Id 属性只有 Get 函数,没有 Let 过程,因此 Id 是只读的。VBA 的类都有一个初始化事件,这个事件会在实例化时触发。我们可以写一个事件处理程序,以便在事件发生时对实例(对象)做一些初始化工作。对只读属性 Id 内部变量 m_Id 的初始化应该在类进行实例化时完成,这样就可以在每次实例化时自动完成对 m_Id 的初始化。这个初始化 m_Id 的工作正好就可以在类初始化事件处理程序中完成,如图1 的 Sub Class_Initialize 过程所示。Private Sub Class_Initialize()...End Sub
事件处理过程的名称必须是 Class_Initialize。初始化事件在 New 的过程中触发,此时 New 的过程还没有完成,只有在我们的初始化事件处理程序结束后,类的实例化才算完成,New 才会返回对象的地址并赋给对象变量。世上的事总是成对出现的,事件也不例外。既然有初始化事件(Initialize event),就必然会有终止化事件(Terminate event)。对象终止化事件的触发点有两个:- 通过将对象变量设置为 Nothing 从而取消了对对象的一切引用时
Sub TermTest() Dim o AsNew Collection Dim sco As SimpleClassSet sco =New SimpleClass ' 触发 SimpleClass 的初始化事件 sco.Name = "ok" Set sco = Nothing ' 触发 sco 对象终止化事件 o.Add "Hello" ' 触发 Collection 的初始化事件End Sub ' o 超出作用域,触发终止化事件
在这个例子中,因为 o 声明为 New Collection,但 Collection 类的实例化并不在此时(即 Dim 声明语句)中完成,而是在第一次对 o 的访问时完成的,即在 o.Add "Hello" 语句处完成的,因此,Collection 的初始化事件也是在此时触发的。因为代码中明确写了 Set sco = Nothing 语句,此语句的执行就断开了 sco 与 sco 所指向的对象的联系,而该对象只有 sco 指向它,因此将触发对象终止化事件。代码中没有写 Set o = Nothing,但在 End Sub 处对象变量 o 超出作用域(o 的作用域仅限于 Sub TermTest() 过程),因此 o 所指向的对象将在此时触发终止化事件。在初始化事件处理程序中,我们会写一些初始化代码,譬如图1 所示的对 m_Id 变量的初始化。在终止化事件处理程序中则是写一些释放资源的代码,譬如将对象变量赋值 Nothing,关闭文件,等待。Private Sub Class_Terminate()...End Sub
终止化事件的触发有一个例外:如果程序非正常终止,则 VBA 在撤销对象的过程中并不触发终止化事件。图2 带有初始化事件和终止化事件处理程序的 NotSimpleClass我们看到,只有初始化事件处理程序执行了,终止化事件处理程序没有执行,说明没有触发终止化事件,因为在测试代码的中途,我们执行了 End 语句,非正常终止了 NotSimpleClassTest 过程的执行。注:虽然初始化事件和终止化事件的处理程序都是以 Class 开头,但这两个事件都是对象事件(类只是一个类型定义,没有生成死灭的活动,也就不会有事件发生):一个是创造对象时触发的事件,触发此事件的目的是让程序员有机会添加一些初始化代码,如我们的例子所示。一个是销毁对象时触发的事件,触发此事件的目的是让程序员有机会在对象销毁时释放该对象保有的资源,如打开的文件等。