如何在VBA中创建合适的集合?

我正在尝试将大型 3 维数组转换为一系列类模块。我将每个下一个类存储为上一个类中的数组。它就像品牌 -> 产品 -> 批次。

我已经成功创建了这个交互并且可以通过名称访问它们,例如:

Sub test()
    Dim MyBrand As Brand
    Set MyBrand = New Brand
    MyBrand.Name = "Company1"
    MyBrand.AddProduct "Shoes"
    MyBrand.Products("Shoes").AddLot "240502"
    MsgBox MyBrand.Products("Shoes").Lots(0) 'Correctly Displays "240502"
End Sub

但后来我想创建一个对象组,可以保存多个Brand对象并像Brands("Company1").

如果我在类模块中使用数组,我最终会得到Brands.Brand("Company1"). 如果我使用Collection,我将不得不使用像Brands(1).

有没有办法创建合适的对象组,以便我可以模仿 Application.Workbooks 等组的语法并按名称引用成员?

回答

自定义集合背后的许多魔力取决于您无法在 VBE 中编辑的隐藏属性;您需要导出(并在出现提示时从项目中删除)类模块,在 Notepad/Notepad++ 中编辑其魔法成员属性,保存更改,然后将模块重新导入到项目中。

这显然很乏味且容易出错,但有一种(好多)更好的方法。

为了支持这一点:

Set shoesProduct = MyBrand.Products("Shoes")

您可以定义Products为 aDictionary并称其为一天,但是作为概念的封装是......好吧,在这里受到打击(内部集合是 a Dictionary, aCollection还是 .NETArrayList通常应该是其余部分的实现细节的代码不需要关心)。

我怀疑这个Brand类有太多的责任,并且“是”产品集合;最佳做法是将Brand.Products属性定义如下:

Public Property Get Products() As Products

因此,您需要有一个Products类(非常类似于Workbook.WorksheetsWorkbook.Sheets属性都返回一个Sheets集合对象),该类封装了一个私有的模块级VBA.Collection字段(可能有键,但您不能访问或迭代集合的键)。

Products自定义集合类需要一个Item 默认属性(这个名字Item是一个约定); 该实现只是从私有封装中提取项目Collection

'@DefaultMember
Public Property Get Item(ByVal Index As Variant) As Product
    Set Item = ThePrivateCollection.Item(Index)
End Property

如果您使用Rubberduck,此@DefaultMember注释/评论将触发有关注释和相应隐藏属性“不同步”的检查结果;右键单击该检查结果并选择“调整属性值”,让 Rubberduck 为您生成隐藏代码并处理烦人的导出/删除-编辑-重新导入循环。

否则,您需要手动编辑隐藏VB_UserMemId成员属性,使其成为类的默认成员

Public Property Get Item(ByVal Index As Variant) As Product
    Attribute Item.VB_UserMemId = 0
    Set Item = ThePrivateCollection.Item(Index)
End Property

有了这个,MyBrand.Products("Shoes")就相当于MyBrand.Products.Item("Shoes").

也许您也想迭代集合中的所有产品?

For Each Product In MyBrand.Products
    Debug.Print Product.Name
Next

为了做到这一点,您需要一个特殊的“枚举器”成员,它从封装的集合中转发枚举器:

'@Enumerator
Public Property Get NewEnum() As IUnknown
    Set NewEnum = ThePrivateCollection.[_NewEnum]
End Property

同样,Rubberduck 注释极大地简化了此操作,但是 Rubberduck 所做的一切,如果您愿意,也可以手动执行:

Public Property Get NewEnum() As IUnknown
    Attribute NewEnum.VB_UserMemId = -4
    Set NewEnum = ThePrivateCollection.[_NewEnum]
End Sub

现在For Each迭代适用于您的自定义对象集合!

如果 aLot不仅仅是一个String值(即实际的对象类型),那么Product该类也可以使用Lots自定义集合 - 但由于 aLot真的只是一个String值(或者是吗?),那么Product可以简单地封装 a Dictionary,并有一个Lots公开Items数组的属性:

Public Property Get Lots() As Variant
    Lots = ThePrivateLotsDictionary.Items
End Property

请注意,这比使用 a 更简单Collection,因为对于一个集合,您需要迭代它并将每个项目复制到一个数组中,以便在不暴露集合本身的情况下返回这些项目(暴露Lots() As Collection使AddLot成员完全多余)。

至于Brands集合本身,请注意Tim Williams 的建议并使用Dictionary数据结构。

  • When I first saw your answer I had to set a reminder to come back after gaining more experience with class modules. After half a month of study, I feel like I'm still only halfway to being able to comprehend the wisdom contained within it. Thank you for the great answer, I'll have to come back again after studying some more.

以上是如何在VBA中创建合适的集合?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>