今天我们讨论数组作为 Sub/Function 参数的情形。Sub PrintArray(ByRef A() As Integer) Dim i As IntegerFori= LBound(A) to UBound(A) Debug.Print A(i) Next iEnd Sub
过程 PrintArray 用来打印作为参数的数组。这样声明的数组参数,不管调用方的数组是静态数组(固定数组)还是动态数据,都可以使用 PrintArray 过程把数组元素打印出来。注意,数组作为参数时,必须声明为 ByRef,虽然 VBA 默认的传递方式就是 ByRef,似乎省略 ByRef 也可以,但最好还是明确声明。因为数组一般来讲是一个大型数据结构,而我们知道,使用 ByVal 的参数在传递时是要将实参拷贝到过程中的形参。这个拷贝过程对于数组这样的大型数据结构开销过大,因此 VBA 规定凡是数组参数都要使用传地址的 ByRef 方式,以避免数组的拷贝。另外,PrintArray 过程的数组参数声明为 Integer,这意味着只有 Integer 类型的数组才能使用 PrintArray 打印,任何其他类型都不允许,甚至连隐式类型转换都没有,实参与形参的类型必须精确匹配。显然这种规定过于死板。有没有变通的办法呢?Sub PrintArray2(ByRef A() As Variant) Dim i As IntegerFori= LBound(A) to UBound(A) Debug.Print A(i) Next iEnd Sub
在这第二版中,我们把数组参数声明为变体类型(Variant),这似乎应该可以了。但实际测试表明,只有 Variant 数组才能作为 PrintArray2 的实参,其他任何类型的数组都不能作为实参调用 PrintArray2。虽然可以将数组都声明为 Variant,但我们不希望这样,毕竟 Variant 类型在运行过程中要损失不少效率,不能为了使用 PrintArray2 过程而将所有数组都声明为效率低下的 Variant 类型。Sub PrintArray3(ByRef A As Variant) Dim i As Integer If IsArray(A) ThenFori= LBound(A) to UBound(A) Debug.Print A(i) Next i End IfEnd Sub
PrintArray3 与 PrintArray2 的区别只在于参数声明中的 A() 变成了 A。这是将数组 A 作为一个整体声明为 Variant 类型。因为 Variant 类型可以包容任何类型,甚至数组类型,所以此时任何类型的变量都可以作为实参调用 PrintArray3。所以过程体中增加了对 A 的判断:用 IsArray 函数判断实参是不是一个数组,如果是数组,则进入 For 循环,逐个打印数组元素。从这个例子中,我们可以看出,将参数声明为 A As Variant,则不仅数组可以作为实参调用 PrintArray3,其他任何参数也都可以作为实参调用 PrintArray3。因此,在 PrintArray3 中分别不同情形做了相应处理:如果是数组,则通过 For ... Next For 循环打印出整个数组,否则,直接打印作为单个值的实参。测试结果也确实如此:- 整数数组 myA 作为实参,则在 PrintArray3 内测得的结果是:A(myA)的类型是一个 Integer 数组,实际这是一个包裹在 Variant 类型内的数组,因此其 VarType 的结果是 8194 = 8192(vbArray)+ 2(vbInteger)。其中,vbArray 指的是 A 是一个包裹数组的 Variant 类型,vbInteger 指的是数组的元素类型是 Integer。
- 字符串数组 myB 作为实参,则在 PrintArray3 内测得的结果是:A(myB)的类型是一个 String 数组,其 VarType 的结果是 8200 = 8192(vbArray)+ 8(vbString)。其中,vbArray 指的是 A 是一个包裹数组的 Variant 类型,vbString 指的是数组元素类型是 String。
- Variant 类型 v 作为实参,则在 PrintArray3 内测得的是:A(v)的类型是一个 Date 变量,其 VarType 的结果是 7(vbDate),说明这是一个包裹 Date 类型的 Variant 类型。
另外,本例还说明,动态数组 myA 和静态数组 myB 在 PrintArray3 看来没有区别。这种方法的唯一限制是:数组的类型只能是 VBA 预定义类型,任何用户定义类型(UDT)数组都不能作为实参调用 PrintArray3,见下图:图2 UDT 数组不能作为实参调用 PrintArray3图中,myA 是 MathScore 类型的数组,调用 PrintArray3 时出错。解决这个问题的办法是将 MathScore 定义为类类型(class type)。这个以后讲到类的时候再说。