限制类模块中Collection的类型

时间:2011-04-17 19:45:32

标签: vba collections types

我在类模块中有一个集合。我想限制对这个集合“可添加”的对象类型,即集合应该只接受一个给定类型的对象,而不是其他任何东西。

有没有办法强制添加到集合中的对象类型?

据我所知,没有内置的方法可以做到这一点。然后解决方案是将此集合设为私有,并为通常可用于集合的方法构建包装函数,即AddRemoveItemCount

我讨厌必须编写3个不添加任何功能的包装函数,只是为了能够为Add方法添加一些类型强制执行。但如果这是唯一的方式,那么这是唯一的方法。

4 个答案:

答案 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这样的集合来说是一个更强大的解决方案。