VBA集合:密钥列表

Art*_*wan 43 collections vba

在我向VBA集合添加一些值之后,有没有办法保留所有键的列表?

例如

Dim coll as new  Collection
Dim str1, str2, str3
str1="first string"
str2="second string"
str3="third string"
coll.add str1, "first key"
coll.add str2, "second key"
coll.add str3, "third key"
Run Code Online (Sandbox Code Playgroud)

我知道如何保留字符串列表:

first string
second string
third string
Run Code Online (Sandbox Code Playgroud)

再一次:有没有办法保留钥匙?

first key
second key
third key
Run Code Online (Sandbox Code Playgroud)

注意:我正在通过AutoCAD 2007使用VBA

GSe*_*erg 41

如果您打算使用默认的VB6 Collection,那么最简单的方法是:

col1.add array("first key", "first string"), "first key"
col1.add array("second key", "second string"), "second key"
col1.add array("third key", "third string"), "third key"
Run Code Online (Sandbox Code Playgroud)

然后,您可以列出所有值:

Dim i As Variant

For Each i In col1
  Debug.Print i(1)
Next
Run Code Online (Sandbox Code Playgroud)

或所有键:

Dim i As Variant

For Each i In col1
  Debug.Print i(0)
Next
Run Code Online (Sandbox Code Playgroud)

  • 使用它时你可能更喜欢使用**VBA.Array**,即`col1.add VBA.Array("第一个键","第一个字符串"),"第一个键"`,以避免由"引起的不同下界"选项基础1".这样,结果数组的下限将始终为**0** (4认同)
  • 我以前使用过这个解决方案。我正在寻找更多_漂亮_的东西。不过还是谢谢你 :) (2认同)
  • 不错的解决方案,对我来说似乎很漂亮,词典可能会更强大,但这可以在没有Windows脚本(即Mac OS)外部依赖的情况下运行,并且不需要额外的维护或其他类别 (2认同)

Ale*_* K. 38

我不认为使用vanilla集合可能而不将键值存储在独立数组中.

执行此操作的最简单方法是添加对Microsoft Scripting Runtime的引用,并使用功能更强大的Dictionary:

Dim dict As Dictionary
Set dict = New Dictionary

dict.Add "key1", "value1"
dict.Add "key2", "value2"

Dim key As Variant
For Each key In dict.Keys
    Debug.Print "Key: " & key, "Value: " & dict.Item(key)
Next
Run Code Online (Sandbox Code Playgroud)

  • 只是为了澄清,这种方法应该适用于所有Windows操作系统,但它不适用于Mac OS. (8认同)
  • 为了跨平台兼容性,您可以使用类模块。请参阅:/sf/ask/1390848651/ GitHub 上提供的源代码:https://github.com/VBA-tools/VBA-Dictionary (3认同)
  • 它能在100%的每台计算机上运行吗? (2认同)

小智 10

您可以创建一个小类来保存键和值,然后将该类的对象存储在集合中.

类KeyValue:

Public key As String
Public value As String
Public Sub Init(k As String, v As String)
    key = k
    value = v
End Sub
Run Code Online (Sandbox Code Playgroud)

然后使用它:

Public Sub Test()
    Dim col As Collection, kv As KeyValue
    Set col = New Collection
    Store col, "first key", "first string"
    Store col, "second key", "second string"
    Store col, "third key", "third string"
    For Each kv In col
        Debug.Print kv.key, kv.value
    Next kv
End Sub

Private Sub Store(col As Collection, k As String, v As String)
    If (Contains(col, k)) Then
        Set kv = col(k)
        kv.value = v
    Else
        Set kv = New KeyValue
        kv.Init k, v
        col.Add kv, k
    End If
End Sub

Private Function Contains(col As Collection, key As String) As Boolean
    On Error GoTo NotFound
    Dim itm As Object
    Set itm = col(key)
    Contains = True
MyExit:
    Exit Function
NotFound:
    Contains = False
    Resume MyExit
End Function
Run Code Online (Sandbox Code Playgroud)

这当然类似于Dictionary建议,除了没有任何外部依赖.如果要存储更多信息,可以根据需要使类更复杂.


小智 8

另一种解决方案是将密钥存储在单独的集合中:

'Initialise these somewhere.
Dim Keys As Collection, Values As Collection

'Add types for K and V as necessary.
Sub Add(K, V) 
Keys.Add K
Values.Add V, K
End Sub
Run Code Online (Sandbox Code Playgroud)

您可以为键和值维护单独的排序顺序,这有时很有用.


Chr*_*tor 7

您可以使用 RTLMoveMemory 在您的内存中窥探并直接从那里检索所需的信息:

32 位:

Option Explicit

'Provide direct memory access:
Public Declare Sub MemCopy Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByVal Destination As Long, _
    ByVal Source As Long, _
    ByVal Length As Long)


Function CollectionKeys(oColl As Collection) As String()

    'Declare Pointer- / Memory-Address-Variables
    Dim CollPtr As Long
    Dim KeyPtr As Long
    Dim ItemPtr As Long

    'Get MemoryAddress of Collection Object
    CollPtr = VBA.ObjPtr(oColl)

    'Peek ElementCount
    Dim ElementCount As Long
    ElementCount = PeekLong(CollPtr + 16)

        'Verify ElementCount
        If ElementCount <> oColl.Count Then
            'Something's wrong!
            Stop
        End If

    'Declare Simple Counter
    Dim index As Long

    'Declare Temporary Array to hold our keys
    Dim Temp() As String
    ReDim Temp(ElementCount)

    'Get MemoryAddress of first CollectionItem
    ItemPtr = PeekLong(CollPtr + 24)

    'Loop through all CollectionItems in Chain
    While Not ItemPtr = 0 And index < ElementCount

        'increment Index
        index = index + 1

        'Get MemoryAddress of Element-Key
        KeyPtr = PeekLong(ItemPtr + 16)

        'Peek Key and add to temporary array (if present)
        If KeyPtr <> 0 Then
           Temp(index) = PeekBSTR(KeyPtr)
        End If

        'Get MemoryAddress of next Element in Chain
        ItemPtr = PeekLong(ItemPtr + 24)

    Wend

    'Assign temporary array as Return-Value
    CollectionKeys = Temp

End Function


'Peek Long from given MemoryAddress
Public Function PeekLong(Address As Long) As Long

  If Address = 0 Then Stop
  Call MemCopy(VBA.VarPtr(PeekLong), Address, 4&)

End Function

'Peek String from given MemoryAddress
Public Function PeekBSTR(Address As Long) As String

    Dim Length As Long

    If Address = 0 Then Stop
    Length = PeekLong(Address - 4)

    PeekBSTR = Space(Length \ 2)
    Call MemCopy(VBA.StrPtr(PeekBSTR), Address, Length)

End Function
Run Code Online (Sandbox Code Playgroud)

64 位:

Option Explicit

'Provide direct memory access:
Public Declare PtrSafe Sub MemCopy Lib "kernel32" Alias "RtlMoveMemory" ( _
     ByVal Destination As LongPtr, _
     ByVal Source As LongPtr, _
     ByVal Length As LongPtr)



Function CollectionKeys(oColl As Collection) As String()

    'Declare Pointer- / Memory-Address-Variables
    Dim CollPtr As LongPtr
    Dim KeyPtr As LongPtr
    Dim ItemPtr As LongPtr

    'Get MemoryAddress of Collection Object
    CollPtr = VBA.ObjPtr(oColl)

    'Peek ElementCount
    Dim ElementCount As Long
    ElementCount = PeekLong(CollPtr + 28)

        'Verify ElementCount
        If ElementCount <> oColl.Count Then
            'Something's wrong!
            Stop
        End If

    'Declare Simple Counter
    Dim index As Long

    'Declare Temporary Array to hold our keys
    Dim Temp() As String
    ReDim Temp(ElementCount)

    'Get MemoryAddress of first CollectionItem
    ItemPtr = PeekLongLong(CollPtr + 40)

    'Loop through all CollectionItems in Chain
    While Not ItemPtr = 0 And index < ElementCount

        'increment Index
        index = index + 1

        'Get MemoryAddress of Element-Key
        KeyPtr = PeekLongLong(ItemPtr + 24)

        'Peek Key and add to temporary array (if present)
        If KeyPtr <> 0 Then
           Temp(index) = PeekBSTR(KeyPtr)
        End If

        'Get MemoryAddress of next Element in Chain
        ItemPtr = PeekLongLong(ItemPtr + 40)

    Wend

    'Assign temporary array as Return-Value
    CollectionKeys = Temp

End Function


'Peek Long from given Memory-Address
Public Function PeekLong(Address As LongPtr) As Long

  If Address = 0 Then Stop
  Call MemCopy(VBA.VarPtr(PeekLong), Address, 4^)

End Function

'Peek LongLong from given Memory Address
Public Function PeekLongLong(Address As LongPtr) As LongLong

  If Address = 0 Then Stop
  Call MemCopy(VBA.VarPtr(PeekLongLong), Address, 8^)

End Function

'Peek String from given MemoryAddress
Public Function PeekBSTR(Address As LongPtr) As String

    Dim Length As Long

    If Address = 0 Then Stop
    Length = PeekLong(Address - 4)

    PeekBSTR = Space(Length \ 2)
    Call MemCopy(VBA.StrPtr(PeekBSTR), Address, CLngLng(Length))

End Function
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢低级,谢谢。有没有办法给这个添加书签? (2认同)
  • 都是我自己想出来的!;-D (2认同)