【问题标题】:Restrict type in a Collection inside a class module限制类模块内集合中的类型
【发布时间】:2011-04-17 19:45:31
【问题描述】:

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

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

据我所知,没有内置的方法可以做到这一点。那么解决方案是将这个集合设为私有,并为集合通常可访问的方法构建包装函数吗,即AddRemoveItemCount

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

【问题讨论】:

    标签: vba collections types


    【解决方案1】:

    没有办法避免包装函数。这只是 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 的所有功能。)

    【讨论】:

    • 为什么每个“自定义集合”都需要自己的模块?我不能从现在开始制作一个通用的CustomCollection.cls 类并使用它而不是Collection 吗?无论如何,很好的答案,+1
    • 是的,没错,我第一次就明白了。我的意思是:为什么不创建一个通用的 UniformCollection 类,它可以包含 any 对象类型,只要它对于集合中的所有项目都是相同的类型?我实际上是这样做的;稍后将显示在答案中。
    【解决方案2】:

    这就是我所做的。正如@jtolle 所指出的,我喜欢Rob van Gelder 的example,但我为什么要满足于制作一个永远只接受一种特定对象类型(例如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 没有一路走好!更糟糕的是,微软没有将这种东西作为内置功能包含在内......

    【讨论】:

    • +1 为 VBA 特定问题提供有趣的 VBA 特定解决方案。
    • 我认为如果您将添加的第一件事视为您的“原型”,它可能会稍微干净一些。但我喜欢这种东西。 “只是”在集合中提取运行时类型检查不会让您获得编译时类型安全,但如果您能弄清楚如何实现,它仍然可以让您实现 Collection 接口 (stackoverflow.com/questions/5748085/…)。
    • 我喜欢使用第一个添加的项目作为原型定义的想法!星期二必须记住这一点。
    【解决方案3】:

    我编写了几乎与 Jean-François Corbett 相同的代码,但我适应了,因为由于某种原因无法正常工作。

    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
    

    然后,当我想从 CCollection 创建一个实例时,我制作如下代码:

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

    CEmpresa 是我想要的对象的类类型

    【讨论】:

      【解决方案4】:

      是的。解决方案是将您的收藏设为私有,然后将公共包装函数设为添加、删除、getitem 和计数等。

      编写附加代码可能看起来很麻烦,但它是encapsulate 这样的集合的更强大的解决方案。

      【讨论】:

        猜你喜欢
        • 2019-04-25
        • 1970-01-01
        • 2016-01-09
        • 1970-01-01
        • 2013-06-03
        • 2014-05-02
        • 2021-09-19
        • 2015-02-26
        • 1970-01-01
        相关资源
        最近更新 更多