Tod*_*son 2 collections excel 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
Run Code Online (Sandbox Code Playgroud)
但后来我想创建一个对象组,可以保存多个Brand对象并像Brands("Company1").
如果我在类模块中使用数组,我最终会得到Brands.Brand("Company1"). 如果我使用Collection,我将不得不使用像Brands(1).
有没有办法创建合适的对象组,以便我可以模仿 Application.Workbooks 等组的语法并按名称引用成员?
自定义集合背后的许多魔力取决于您无法在 VBE 中编辑的隐藏属性;您需要导出(并在出现提示时从项目中删除)类模块,在 Notepad/Notepad++ 中编辑其魔法成员属性,保存更改,然后将模块重新导入到项目中。
这显然很乏味且容易出错,但有一种(好多)更好的方法。
为了支持这一点:
Set shoesProduct = MyBrand.Products("Shoes")
Run Code Online (Sandbox Code Playgroud)
您可以定义Products为 aDictionary并称其为一天,但是作为概念的封装是......好吧,在这里受到打击(内部集合是 a Dictionary, aCollection还是 .NETArrayList通常应该是其余部分的实现细节的代码不需要关心)。
我怀疑这个Brand类有太多的责任,并且“是”产品集合;最佳做法是将Brand.Products属性定义如下:
Public Property Get Products() As Products
Run Code Online (Sandbox Code Playgroud)
因此,您需要有一个Products类(非常类似于Workbook.Worksheets和Workbook.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
Run Code Online (Sandbox Code Playgroud)
如果您使用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
Run Code Online (Sandbox Code Playgroud)
有了这个,MyBrand.Products("Shoes")就相当于MyBrand.Products.Item("Shoes").
也许您也想迭代集合中的所有产品?
For Each Product In MyBrand.Products
Debug.Print Product.Name
Next
Run Code Online (Sandbox Code Playgroud)
为了做到这一点,您需要一个特殊的“枚举器”成员,它从封装的集合中转发枚举器:
'@Enumerator
Public Property Get NewEnum() As IUnknown
Set NewEnum = ThePrivateCollection.[_NewEnum]
End Property
Run Code Online (Sandbox Code Playgroud)
同样,Rubberduck 注释极大地简化了此操作,但是 Rubberduck 所做的一切,如果您愿意,也可以手动执行:
Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
Set NewEnum = ThePrivateCollection.[_NewEnum]
End Sub
Run Code Online (Sandbox Code Playgroud)
现在For Each迭代适用于您的自定义对象集合!
如果 aLot不仅仅是一个String值(即实际的对象类型),那么Product该类也可以使用Lots自定义集合 - 但由于 aLot真的只是一个String值(或者是吗?),那么Product可以简单地封装 a Dictionary,并有一个Lots公开Items数组的属性:
Public Property Get Lots() As Variant
Lots = ThePrivateLotsDictionary.Items
End Property
Run Code Online (Sandbox Code Playgroud)
请注意,这比使用 a 更简单Collection,因为对于一个集合,您需要迭代它并将每个项目复制到一个数组中,以便在不暴露集合本身的情况下返回这些项目(暴露Lots() As Collection使AddLot成员完全多余)。
至于Brands集合本身,请注意Tim Williams 的建议并使用Dictionary数据结构。