Restrict type in a Collection inside a class module

后端 未结 4 1518
暖寄归人
暖寄归人 2020-12-16 06:36

I have a collection inside a class module. I\'d like to restrict the object type that is \"addable\" to this collection, i.e. collection should only ever accept objects of o

相关标签:
4条回答
  • 2020-12-16 06:59

    Yes. The solution is to make your collection private and then make public wrapper functions to add, remove, getitem and count etc.

    It may seem like hassle to write the additional code but it is a more robust solution to encapsulate the collection like this.

    0 讨论(0)
  • 2020-12-16 07:00

    This is what I did. I liked Rob van Gelder's example, as pointed to by @jtolle, but why should I be content with making a "custom collection class" that will only accept one specific object type (e.g. People), forever? As @jtolle points out, this is super annoying.

    Instead, I generalized the idea and made a new class called UniformCollection that can contain any data type -- as long as all items are of the same type in any given instance of UniformCollection.

    I added a private Variant that is a placeholder for the data type that a given instance of UniformCollection can contain.

    Private mvarPrototype As Variant
    

    After making an instance of UniformCollection and before using it, it must be initialized by specifying which data type it will contain.

    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
    

    The Add method will then only accept new items that are of the same type as Prototype (be it an object or a primitive variable... haven't tested with UDTs yet).

    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
    

    The rest is pretty much the same as in the example (plus some error handling). Too bad RvG didn't go the whole way! Even more too bad that Microsoft didn't include this kind of thing as a built-in feature...

    0 讨论(0)
  • 2020-12-16 07:03

    There is no way to avoid wrapper functions. That's just inherent in the "specialization through containment/delegation" model that VBA uses.

    You can build a "custom collection class", though. You can even make it iterable with For...Each, but that requires leaving the VBA IDE and editing source files directly.

    First, see the "Creating Your Own Collection Classes" section of the old Visual Basic 6.0 Programmer's Guide:

    http://msdn.microsoft.com/en-us/library/aa262340(v=VS.60).aspx

    There is also an answer here on stackoverflow that describes the same thing:

    vb6 equivalent to list<someclass>

    However, those are written for VB6, not VBA. In VBA you can't do the "procedure attributes" part in the IDE. You have to export the class module as text and add it in with a text editor. Dick Kusleika's website Daily Dose of Excel (Dick is a regular stackoverflow contributer as you probably know) has a post from Rob van Gelder showing how to do this:

    http://www.dailydoseofexcel.com/archives/2010/07/04/custom-collection-class/

    In your case, going to all that trouble - each "custom collection" class needs its own module - might not be worth it. (If you only have one use for this and it is buried in another class, you might find that you don't want to expose all of the functionality of Collection anyway.)

    0 讨论(0)
  • 2020-12-16 07:22

    I did almost the same code of Jean-François Corbett, but I adapted because for some reason wasn't working.

    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
    

    Then, when i want to create an instance from CCollection I make like the code bellow:

        Set pFornecedores = New CCollection
        pFornecedores.Inicializar ("CEmpresa")
    

    Where CEmpresa is the class type from the object I want

    0 讨论(0)
提交回复
热议问题