我在类模块中有一个集合。我想限制对这个集合“可添加”的对象类型,即集合应该只接受一个给定类型的对象,而不是其他任何东西。
有没有办法强制添加到集合中的对象类型?
据我所知,没有内置的方法可以做到这一点。然后解决方案是将此集合设为私有,并为通常可用于集合的方法构建包装函数,即Add
,Remove
,Item
和Count
?
我讨厌必须编写3个不添加任何功能的包装函数,只是为了能够为Add
方法添加一些类型强制执行。但如果这是唯一的方式,那么这是唯一的方法。
答案 0 :(得分:5)
无法避免包装函数。这只是VBA使用的“通过遏制/授权专业化”模式所固有的。
你可以构建一个“自定义集合类”。您甚至可以使用For...Each
进行迭代,但这需要离开VBA IDE并直接编辑源文件。
首先,请参阅旧版Visual Basic 6.0程序员指南的“创建自己的集合类”部分:
http://msdn.microsoft.com/en-us/library/aa262340(v=VS.60).aspx
还有一个关于stackoverflow的答案描述了同样的事情:
vb6 equivalent to list<someclass>
然而,这些是为VB6编写的,而不是VBA。在VBA中,您无法在IDE中执行“过程属性”部分。您必须将类模块导出为文本并使用文本编辑器将其添加。 Dick Kusleika的网站Daily Dose of Excel(Dick是一个常见的stackoverflow贡献者,你可能知道)有一篇来自Rob van Gelder的文章,展示了如何做到这一点:
http://www.dailydoseofexcel.com/archives/2010/07/04/custom-collection-class/
在您的情况下,遇到所有麻烦 - 每个“自定义集合”类都需要自己的模块 - 可能不值得。 (如果你只有一个用途并且它被隐藏在另一个类中,你可能会发现你想要无论如何都要公开Collection
的所有功能。)
答案 1 :(得分:3)
这就是我所做的。我喜欢Rob van Gelder的example,正如@jtolle指出的那样,但为什么我要满足于制作一个只能接受一种特定对象类型(例如People
)的“自定义集合类” ?正如@jtolle指出的那样,这非常烦人。
相反,我概括了这个想法,并创建了一个名为UniformCollection
的新类,它可以包含任何数据类型 - 只要所有项在UniformCollection
的任何给定实例中都是相同的类型。
我添加了一个私有Variant,它是UniformCollection
的给定实例可以包含的数据类型的占位符。
Private mvarPrototype As Variant
在创建UniformCollection
的实例之后,在使用之前,必须通过指定它将包含的数据类型来初始化它。
Public Sub Initialize(Prototype As Variant)
If VarType(Prototype) = vbEmpty Or VarType(Prototype) = vbNull Then
Err.Raise Number:=ERR__CANT_INITIALIZE, _
Source:=TypeName(Me), _
Description:=ErrorDescription(ERR__CANT_INITIALIZE) & _
TypeName(Prototype)
End If
' Clear anything already in collection.
Set mUniformCollection = New Collection
If VarType(Prototype) = vbObject Or VarType(Prototype) = vbDataObject Then
' It's an object. Need Set.
Set mvarPrototype = Prototype
Else
' It's not an object.
mvarPrototype = Prototype
End If
' Collection will now accept only items of same type as Prototype.
End Sub
Add方法只接受与Prototype类型相同的新项目(无论是对象还是原始变量......还没有使用UDT进行测试)。
Public Sub Add(NewItem As Variant)
If VarType(mvarPrototype) = vbEmpty Then
Err.Raise Number:=ERR__NOT_INITIALIZED, _
Source:=TypeName(Me), _
Description:=ErrorDescription(ERR__NOT_INITIALIZED)
ElseIf Not TypeName(NewItem) = TypeName(mvarPrototype) Then
Err.Raise Number:=ERR__INVALID_TYPE, _
Source:=TypeName(Me), _
Description:=ErrorDescription(ERR__INVALID_TYPE) & _
TypeName(mvarPrototype) & "."
Else
' Object is of correct type. Accept it.
' Do nothing.
End If
mUniformCollection.Add NewItem
End Sub
其余部分与示例中的几乎相同(加上一些错误处理)。太糟糕的RvG没有全力以赴!更糟糕的是微软没有把这种东西作为内置功能包括在内......
答案 2 :(得分:1)
我做了几乎相同的Jean-FrançoisCorbett的代码,但我改编了,因为某些原因不起作用。
SIGN_IN_SUCCESS
然后,当我想从CCollection创建一个实例时,我就像下面的代码一样:
Option Explicit
Public pParametro As String
Private pColecao As New Collection
Public Sub Inicializar(ByVal parametro As String)
pParametro = parametro
End Sub
Public Sub Add(NewItem As Object)
If TypeName(NewItem) <> pParametro Then
MsgBox "Classe do objeto não é compatível à coleção"
Else
pColecao.Add NewItem
End If
End Sub
Public Property Get Count() As Long
Count = pColecao.Count
End Property
Public Property Get Item(NameOrNumber As Variant) As Variant
Set Item = pColecao(NameOrNumber)
End Property
Sub Remove(NameOrNumber As Variant)
pColecao.Remove NameOrNumber
End Sub
CEmpresa是我想要的对象的类类型
答案 3 :(得分:0)
是。解决方案是使您的集合成为私有,然后使公共包装函数添加,删除,getitem和计数等。
编写附加代码似乎很麻烦,但它对encapsulate这样的集合来说是一个更强大的解决方案。