我们前面说过,Err 是一个 VBA 内置的 ErrObject 类型的对象,并且是一个单体实例(singleton),也就是说,你不需要创建 Err 对象,而且还是一个全局对象(global scope),也就是说,你在哪里都可以访问 Err 对象。Debug 对象也是 VBA 的内置对象,也不需要创建,而且哪里也都可以访问。但与 Err 对象不同,Debug 没有属性,只有两个方法:- Print:这个方法我们已经很熟悉,在立即窗口中打印出所有的参数。
- Assert:这个方法对参数求值,如果结果为 FALSE,则挂起当前程序的执行,等待用户干预。如下图所示:
在这个例子中,Debug.Assert 方法的参数是 IsNumeric(Adding(a)),这是检查 Adding 函数返回的结果是不是数值类型,如果不是数值类型,则 IsNumeric 函数返回 FALSE,从而 Debug.Assert 挂起程序执行,似乎在告诉用户:这里有错,请检查。Assert 方法的参数是一个 Boolean 类型的表达式,可以由 And/Or 逻辑连接词连接起来的复杂表达式。Assert 对表达式的求值是完全的,也就是说,对所有子表达式都会求值,譬如 A And B 这样的表达式,若 A 求值为 FALSE,虽然此时无论 B 结果如何,整个表达式都是 FALSE,但 Assert 仍然会对 B 求值,然后进行 And 求出整个表达式的值。这一点与 C 不同。Collection 虽然也是 VBA 的内置对象,但与 Err 和 Debug 都不同,Collection 虽然也称为对象,但更像是一个类型,需要创建一个 Collection 类型的对象,才能使用。首先我们用 Dim c As Collection 声明一个类型为 Collection 的变量 c,然后用 Set c = New Collection 创造出变量 c。声明只是告知 VBA,c 是一个 Collection 类型的变量,但此时 c 并不存在,直到执行完 Set c = New Collection 之后,c 才有了具体的存在。接下来是向 c 中增加内容。因为 c 是一个集合类型的对象,是一个容器,其 Add 方法是向 c 这个容器中添加内容。从图上可以看出,我们添加的内容是以 Key/Item 标记的,也就是说,Item 可以通过 Key 来访问,也就是按名字访问。下面,在通过 Set myA(0) = c 语句将 c 这个变量(实际上这是一个对象,因此前面要冠以 Set)赋给数组 myA 的第一个元素之后,马上又通过 Set c = New Collection 创造一个新的对象。虽然这两个对象都用 c 来标识,但实际上是两个不同的对象:这两个对象的内存地址不同,所含有的内容也不同,这一点可以在程序最后的打印输出结果中看出来。Collection 类型的变量 c 和 Collection 对象的关系如下图所示:变量 c 实际上是一个到对象的引用(可以看作为指针)。Set c = New Collection 语句的作用是:在内存中分配一块空间并作初始化,然后将该空间的首地址赋给变量 c。VBA 中的对象,除了 Err 和 Debug 等极少数拿来即用的对象之外,都要通过某种方式创建对象(即某种对象类型的实例),才能使用。创建对象有两种形式:早绑定(early binding)和晚绑定(late binding)。早绑定,即如图2所示,声明对象变量为某种对象的类型,然后使用 New 操作符创建该类型的对象,也可以将两步合为一步,如:Dim c As CollectionSet c =New Collection
- 因为对象变量(譬如 c)有具体类型,因此 VBA 编译器在编译时可以对其进行类型检查,从而可以尽早发现类型错误。
- 因为对象变量有具体类型,VBA 在运行时不用再去推测变量的类型,因此代码的运行效率相对较高。
- 又因为对象变量有具体类型,因此在代码编辑时,VBA 可以提供对象的信息提示,这种提示称为 Intellisense。如下图所示:
由图可见,因为 xlApp 有具体类型(Excel.Application),因此在 xlApp 后面输入点号(.)后,VBA 即弹出列表框,列出 xlApp 对象的所有属性、方法和事件,供你选择。对于 VBA 的内置对象,早绑定即如上面的图2所示。但对于外部对象,则需要添加该对象的引用才能在代码中声明该对象类型的变量。譬如,对于字典(Dictionary)对象,就需要添加对 Microsoft Scripting Runtime 的引用,因为 Dictionary 对象包含在 Microsoft Scripting Runtime 中。添加引用已经在前一篇文章中讲过了,此处不赘。晚绑定使用 CreateObject 来创建对象,因为对象类型在运行时才能确定,因此对象变量声明时无法声明为具体对象类型,只能声明为 Object 或 Variant。具体见下例。所谓晚绑定,就是在运行时才将 dict 绑定到 Scripting.Dictionary 类型,由于编辑时不知道 dict 的具体类型,因此 IntelliSense 不起作用,在 dict 后面输入点号(.)后,VBA 并不像早绑定那样弹出一个列表框。因为没有 IntelliSense 的辅助,难免会产生拼写错误。对于,使用晚绑定,需要对对象的属性、方法、事件要相当熟悉。对于晚绑定,由于不知道变量的具体类型,VBA 编译器在编译时也无法进行类型检查,因此类型错误也会延迟到运行阶段才会发现。晚绑定还有一个缺点,由于类型绑定要延迟到运行阶段,因此 VBA 在运行时需要进行类型推测和检查,这就会影响代码的运行效率。但晚绑定的有点是:由于不需要事先添加对譬如 Microsoft Scripting Runtime 的引用,而是通过 CreateObject 在运行时去寻找合适的 Microsoft Scripting Runtime 组件,因此有利于 VBA 开发的应用的发布和部署,因为所引用的对象组件在每个人的机器上可能处于不同的位置、具有不同的版本,事先引用某个版本、某个位置的对象组件,在发布和部署时有可能造成问题。当然,如果写的代码/应用是给自己用,那自然应该使用早绑定,以尽量利用早绑定的优点。