Date: 08.31.16 - 11:34pm
I love the vb6 collection object. I frequently use it to hold object arrays and cycle through them with for each. Its also nice to have a .count = 0 to see if its empty instead of having to include an AryIsEmpty(ary) function to see if its been initialized yet.
One weak spot of collections is its key mechanism. It kind of sucks in a couple ways. First, it really needs a built in keyExists method, since trying to access an nonexistant key, or trying to add an existing key both raise errors. an ensureUniqueKey(suggested) is also handy.
One other weakness is, there is no way to tell what an item key is once its set. I mean usually you know, or have it stored somewhere else..but what if you had to search a collection for a value and find it..which key was this? I have had a need to translate the two back and forth before.
One more weakness is that you can not use a numeric key, or even a key which starts with a number. I need this fairly often wanting to add keys for things such as window handles, object pointers, database id etc.
So over at vbforums.com this week, some guys have been talking about collections and the internal structures. Elroys post on a Wrapper for VB6 Collections really caught my interest.
Using these internal structures, they figured out a way to enum all the Keys for the objects, get KeyForIndex, ChangeKey, ChangeIndex etc. Very cool stuff.
Many moons ago i wrote my own collection wrapper, but I dont think it ever survived outside of the one or two projects I used it in. If i remember correctly, I couldnt replicate the for each support that I love so much without adding the extra step of making the underlying collection public in the class. I also tried tracking all the keys in a parallel array to have an enumkeys methods or key for index method. It was bulky and not quite stable and what if an item is removed. Not pretty. This is a better way to do it.
One approach I ended up using in these situations was to always use a small class wrapper for data with my own name, key, value elements. I would add them to the vb collection without a key, then access them through my own lookup methods. Works and is stable, but you end up writing lots of stub classes and search methods.
After playing with the class for a while and digesting what was there, I think I have boiled it down to a final incarnation I want to use.
Since collections get used ALLOT, i want to keep the wrapper itself down to a minimum. The extended methods which are cool to have, but not a daily driver will be a separate reusable class that can be loaded on demand.
Dim extras As CCollectionExtender 'initilize the class on demand (memory saver) Property Get ext() As CCollectionExtender If extras Is Nothing Then Set extras = New CCollectionExtender extras.setTarget c End If Set ext = extras End Property
My final layout looks like this.
Add, Clear, fromFile, toFile, Item (default), KeyExists, keyForIndex, Remove, toString, uniqueKey, ext (as above), Count, isEmpty
changeIndex, changeIndexByKey, changeKey, changeKeyByIndex, fromArray, fromFile, indexForKey, KeyExists, keyforIndex, Keys, toArray, toFile, uniqueKey, isEmpty, toString
There is some duplication between the two, thats because the CCollectionExtender can also be used on its own with a regular collection using a setTarget method. No need to tie it so tightly to CollectionEx that it cant stand on its own.
I havent used it in a real project yet, but I think its a keeper.
Lots of really cool stuff! Big thanks goes out to Elroy and all the authors involved in discovering these structures.
After really going through all the methods and digesting what was done, it turns out you only need one extra primitive that mucks with CopyMemory and the internal collection structures.
All the rest of the methods such as enum keys can be accomplished with just the keyFromIndex(index as long).
This methods takes a vb6 collection, and return the key associated with the element at a given index.
Private c as Collection Public Function keyForIndex(index As Long) As String ' Get a key based on its index value. Must be in range, or error. Dim i As Long Dim ptr As Long Dim sKey As String ' If index < 1 Or index > c.Count Then Err.Raise 9 Exit Function End If ' If index <= c.Count / 2 Then ' Start from front. CopyMemory ptr, ByVal ObjPtr(c) + &H18, 4 ' First item pointer of collection header. For i = 2 To index CopyMemory ptr, ByVal ptr + &H18, 4 ' Next item pointer of collection item. Next i Else ' Start from end and go back. CopyMemory ptr, ByVal ObjPtr(c) + &H1C, 4 ' Last item pointer of collection header. For i = c.Count - 1 To index Step -1 CopyMemory ptr, ByVal ptr + &H14, 4 ' Previous item pointer of collection item. Next i End If ' i = StrPtr(sKey) ' Save string pointer because we're going to borrow the string. CopyMemory ByVal VarPtr(sKey), ByVal ptr + &H10, 4 ' Key string of collection item. keyForIndex = Base16Decode(sKey) ' Move key into property's return. CopyMemory ByVal VarPtr(sKey), i, 4 ' Put string pointer back to keep memory straight. End Function
Comments: (1)On 09.01.16 - 12:10pm Dave wrote: