Excel VBA 编程基础 -- 类与对象(七)编程语言中的动态数据结构一般的实现方式是:一次申请分配一块较大的内存块,供添加元素时取用,等到这块内存用完时,再申请分配一块更大的内存块,如此循环。仔细研究 C# 中 List 动态列表的实现,发现 List 初始申请分配的内存空间是 16 字节,然后后续的申请以逐次加倍的方式进行。我们的 CStudents 集合类的优化可以参照 C# List 的实现,只不过我们的 CStudents 的元素类型是已知的,即 CStudent,所以内存申请的单位是 CStudent 对象的个数:初始化时申请 16 个元素,然后依次加倍。修改后的 CStudents 版本我们称为 CStudentsV2,第一部分代码如下:图1 CStudentsV2 第一部分与第一版相比,增加了一个 m_Last 变量,用来记录数组中第一个空位置,m_Slots 则用来记录当前 m_A 数组中还有多少空位置。下面,我们定义了一个常量 DEFAULT_SIZE,表示 m_A 数组最初的大小。然后是初始化事件处理程序 Class_Initialize,在这个过程中,我们对数组 m_A 进行初始化,并对 m_Slots 和 m_Last 赋初值。从初始化过程可以看出,初始化以后,m_A 有 DEFAULT_SIZE 个空位置,并且第一个空位置是 0。另外,我们在 ReDim 中使用了 lower To upper 的表示法,从而屏蔽了 Option Base 的影响。下一个 Sub 就是 Grow,这个过程用来扩张数组 m_A 的空间。首先计算新空间的大小,即当前空间大小的两倍,然后从总大小中减去当前数组已用空间,即为新的空位数,赋给 m_Slots,最后使用 ReDim Preserve 扩张 m_A 的空间。下面看 Add 和 Delete 方法的实现:图2 CStudentsV2 第二部分在 Add 中,首先检查是否还有空位置,m_Slots <= 0 表示已经没有空位,则调用 Grow 过程扩张 m_A。然后将新元素加入 m_A,并更新 m_Slots 和 m_Last:m_Slots 要减 1,m_Last 要加 1,指向下一个空位。在 Delete 中,首先检查 index 参数是否超出范围,若超过范围,则直接返回(你也可以选择用 Err.Raise 报告错误)。若要删除的元素位于最后,则不需要移动元素,否则,将 index 之后的元素逐一前移一个位置。最后,更新 m_Slots 和 m_Last:m_Slots 加 1,m_Last 减 1,后退一个位置,指向当前空出的位置。下面来看 Count 和 Item 属性:图3 CStudentsV2 第三部分因为此时 m_A 中含有空位置,再使用第一版的 Count 实现就不行了。不过现在的 Count 实现更简单了,直接返回 m_Last 即可。因为 m_A 的下界从 0 开始,所以 m_Last 的值就反映了 m_A 中的实际元素数。同样,在 Item 属性的实现中,对 index 的检查也要修改,因为此时 index 的有效范围区间 [ 0, m_last - 1 ]。以上就是优化后 CStudentsV2 的全部内容。下面来看 CStudentsV2 的应用。图4 CStudentsV2 的应用这段代码,除了 CStudents 改成 CStudentsV2 之外,没有做任何修改。如果 CStudents 集合类的名字没有改变的话,则这段代码不需要任何修改。这也就是 OOP 的魅力:外界只需要遵循类/对象所呈现的接口(属性/方法),只要接口不变,则外界代码也就不需要变,而不用管类/对象内部实现经过了怎样的变化。现代语言的集合类都能应用 For Each ... Next 循环来枚举集合中的元素,但我们的 CStudentsV2 不具备这个能力。这是因为 For Each ... Next 要求枚举器(enumerator)实现 IEnumVARIANT 接口,但纯 VBA 代码无法实现这样一个 COM 接口。所有实现集合类的例子都在内部借用了 Collection,利用 Collection 的枚举器来支持 For Each ... Next 枚举循环。我们的 CStudents 因为是使用数组实现的,而数组没有枚举器。VBA 语言有很多不一致的地方,VBA 代码无法实现枚举器从而不支持 For Each ... Next 循环结构就是一个例子。写到这里,我们的《类与对象》系列就结束了,下一篇将返回到《对象》系列,继续讨论 VBA 中提供的各种对象。相关阅读Excel VBA 编程基础 -- 类与对象(一)Excel VBA 编程基础 -- 类与对象(二)Excel VBA 编程基础 -- 类与对象(三)Excel VBA 编程基础 -- 类与对象(四)Excel VBA 编程基础 -- 类与对象(五)Excel VBA 编程基础 -- 类与对象(六)