Dot-Net

用自定義 VBA 類包裝 .Net ArrayList 獲取迭代器

  • April 28, 2015

我剛剛發現我可以使用可以創建 COM 類的 CreateObject 方法從 VBA 創建一些 .Net 類。這很酷,但是創建的類是後期綁定的,所以你不會得到任何智能感知等。所以我希望做的是編寫 VBA 包裝類並將所有方法呼叫委託給 .Net 對象的內部引用。

所以這對於一個 ArrayList 對象來說很好,除了試圖引用列舉器之外。VBA 有一個 hack,它允許您創建自己的集合併使用 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

大多數實現都持有對 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

我很確定它可以完成這項工作,因為可以在沒有包裝器的情況下直接迭代 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

我可以在沒有 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 

最終編輯:感謝@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

當你使用

Dim enumerator As Object

您正在聲明一個 COM 對象。但是 COM 將 IEnumerator 實現為IEnumVARIANT,它直接繼承IUnknown,並且 COM 不樂意嘗試將GetEnumerator返回值設置為Object. 相反,您可以使用

Dim enumerator As IEnumVARIANT

**編輯:**請注意,雖然ArrayListimplements IEnumerable,但它使用採用and的方法重載GetEnumerator。所以當你想呼叫不帶參數的重載時,你必須在 VBA 中使用關鍵字。index``count``Nothing

Set iterator = list.GetEnumerator(Nothing, Nothing)

您可以看到它正在工作,因為如果您包含將兩個項目添加到列表中的原始程式碼行,然後使用諸如

Set iterator = list.GetEnumerator(1, 3) ' 3 is out of bounds

您現在收到一個新的(預期的)錯誤,通知您偏移/長度超出範圍。

引用自:https://stackoverflow.com/questions/25580867