使用自定义VBA类获取迭代器包装.Net ArrayList

Ros*_*sco 10 .net com vba for-loop arraylist

我刚刚发现我可以使用CreateObject方法从VBA创建一些.Net类,它可以创建COM类.这非常酷,但是创建的类是后期绑定的,所以你没有获得任何intellisense等.所以我希望做的是编写VBA包装类并将所有方法调用委托给.Net对象的内部引用.

因此,除了尝试引用枚举器之外,这适用于所有内容的ArrayList对象.有一个VBA的黑客攻击,它允许您创建自己的集合并使用For Each ...语法来枚举您的集合.以下是一个例子:

Public Property Get NewEnum() As IUnknown
    Attribute NewEnum.VB_UserMemId = -4
    Attribute NewEnum.VB_MemberFlags = "40"
    Set NewEnum = <<< pass a reference to the enumerator here.
End Property
Run Code Online (Sandbox Code Playgroud)

大多数实现都包含对VBA Collection对象的引用,并传递Collection对象的枚举器.但是,由于我持有.Net ArrayList,我想将其传递出去.但是,当我尝试时,我得到"无效的过程调用或参数".

我目前的尝试是这样的:

Public Function NewEnum() As IUnknown
    Dim enumerator As Object
    Set enumerator = internalList.GetEnumerator()    <<<< Error occurs here.
    Set NewEnum = enumerator
End Function
Run Code Online (Sandbox Code Playgroud)

我很确定它可以实现这个功能,因为可以在没有包装器的情况下直接迭代ArrayList集合.例如

Public Sub TestCreateArrayList()
    Dim list As Object
    Set list = CreateObject("System.Collections.ArrayList")

    list.Add "an item."
    list.Add "another item."

    Dim Item As Variant
    For Each Item In list
        Debug.Print Item
    Next
End Sub
Run Code Online (Sandbox Code Playgroud)

我可以在没有For Each功能的情况下生活,但如果我可以让它工作,特别是当它看起来几乎就在那里时会很好.

编辑:可能值得一提的是,实例化一个ArrayList然后调用GetEnumerator即使在包装类之外也会产生相同的错误.

进一步编辑:请注意,尝试将IUnknown类型的变量设置为GetEnumerator方法的结果仍会产生相同的错误.

Public Sub TestCreateArrayList()
    Dim list As Object
    Set list = CreateObject("System.Collections.ArrayList")

    Dim iterator As IUnknown
    Set iterator = list.GetEnumerator()   <<<< error occurs here.
End Sub 
Run Code Online (Sandbox Code Playgroud)

最终编辑:感谢@ RegEdit的评论,我能够让这个工作,并认为我会添加代码:

Private internalList As Object

Private Sub Class_Initialize()
    ' create a .Net Array list for the internal data store.
    Set internalList = CreateObject("System.Collections.ArrayList")
End Sub

Private Sub Class_Terminate()
    If Not internalList Is Nothing Then
        On Error Resume Next
        internalList.Dispose
        Err.Clear
    End If
End Sub

Public Function NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
        Dim enumerator As IUnknown
        Set enumerator = internalList.GetEnumerator(0, internalList.Count)
        Set NewEnum = enumerator
End Function

' ... other wrapped methods elided
Run Code Online (Sandbox Code Playgroud)

Reg*_*dit 11

当你使用

 Dim enumerator As Object
Run Code Online (Sandbox Code Playgroud)

你正在声明一个COM对象.但是COM实现了IEnumerator IEnumVARIANT,它IUnknown直接继承,并且COM不乐意尝试将GetEnumerator返回值设置为Object.相反,你可以使用

Dim enumerator As IEnumVARIANT
Run Code Online (Sandbox Code Playgroud)

编辑:请注意,虽然ArrayList实现IEnumerable,它重载GetEnumerator了一个带index和的方法count.因此Nothing,当您想要在没有参数的情况下调用重载时,必须在VBA中使用关键字.

Set iterator = list.GetEnumerator(Nothing, Nothing)
Run Code Online (Sandbox Code Playgroud)

您可以看到它正在工作,因为如果您包含将两个项目添加到列表中的原始代码行,然后使用诸如

Set iterator = list.GetEnumerator(1, 3) ' 3 is out of bounds
Run Code Online (Sandbox Code Playgroud)

您现在得到一个新的(预期的)错误,通知您偏移/长度超出范围.

  • @RegEdit你是一个传奇.这正是问题所在!我甚至没有考虑到VBA可能会尝试使用重载方法.我想如果我没有提供任何参数,它会尝试使用没有参数的那个.所以我使用:set iterator = list.GetEnumerator(0,internalList.Count) (2认同)