I'm on VB.NET 2005 so I have access to generics, but I seem to keep running into trouble when trying to "translate" the VB6 Dictionary to a .NET equivalent.
It all began when translating VB6 "Dictionary" to the .NET:
Dim Pairs As Hashtable
That looked great, until I found out the code relied on the VB6 Dictionary being sorted by the item insertion order, as the indices are later matched against another table. The .NET Hashtable doesn't seem to guarantee such a sort order.
So then I tried with this:
Dim Pairs As List(Of KeyValuePair(String, String))
Great, now I had my key/value pairs in a list that should clearly be in the insertion order (latest item added last). BUT, now I can only retrieve values by their index by the place via an integer, and not via the key name. I checked with Pairs.Find(), but it of course only allows searching for the entire KeyValuePair object, not the key alone, and get the value by it.
So what I'd need is a data structure that fulfills the VB6 Dictionary behavior as in:
- Values can be retrived by integer index
- Values can be retreived by key
- The order must equal the insertion order of the items
Hmm...

VB.NET structure for VB6's integer AND key indexable Dictionary?
Yusufp
Unfortunately, I don't think that any of the collections provided by the .NET framework meet your requirements. There is a generic Dictionary class, but like Hashtable, the ordering of its items is undefined (for purposes of enumeration). I think you have 2 options using .NET:
1) You can sort your tables before you compare them.
2) It's not totally trivial, but you can implement your own 'OrderedDictionary' as below. (NOTE, I just hacked this together as an example, and it might have bugs :)
Public Class OrderedDictionary(Of TKey, TValue)
Implements IDictionary(Of TKey, TValue)
Private m_dictionary As New Dictionary(Of TKey, TValue)
Private m_keysInOrder As New List(Of TKey)
Default Public Overloads Property Item(ByVal index As Integer) As TValue
Get
Return CType(m_dictionary, IDictionary(Of TKey, TValue)).Item(m_keysInOrder(index))
End Get
Set(ByVal value As TValue)
CType(m_dictionary, IDictionary(Of TKey, TValue)).Item(m_keysInOrder(index)) = value
End Set
End Property
Default Public Overloads Property Item(ByVal key As TKey) As TValue Implements IDictionary(Of TKey, TValue).Item
Get
Return CType(m_dictionary, IDictionary(Of TKey, TValue)).Item(key)
End Get
Set(ByVal value As TValue)
CType(m_dictionary, IDictionary(Of TKey, TValue)).Item(key) = value
End Set
End Property
Public Sub Clear() Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Clear
CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).Clear()
m_keysInOrder.Clear()
End Sub
Public Function Contains(ByVal item As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Contains
CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).Contains(item)
End Function
Public Sub CopyTo(ByVal array() As KeyValuePair(Of TKey, TValue), ByVal arrayIndex As Integer) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).CopyTo
CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).CopyTo(array, arrayIndex)
End Sub
Public ReadOnly Property Count() As Integer Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Count
Get
Return CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).Count
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).IsReadOnly
Get
Return CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).IsReadOnly
End Get
End Property
Public Function Remove(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).Remove
Return CType(m_dictionary, IDictionary(Of TKey, TValue)).Remove(key)
m_keysInOrder.Remove(key)
End Function
Public Function Remove(ByVal item As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Remove
CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).Remove(item)
m_keysInOrder.Remove(item.Key)
End Function
Public Sub Add(ByVal key As TKey, ByVal value As TValue) Implements IDictionary(Of TKey, TValue).Add
CType(m_dictionary, IDictionary(Of TKey, TValue)).Add(key, value)
m_keysInOrder.Add(key)
End Sub
Public Sub Add(ByVal item As KeyValuePair(Of TKey, TValue)) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Add
CType(m_dictionary, ICollection(Of KeyValuePair(Of TKey, TValue))).Add(item)
m_keysInOrder.Add(item.Key)
End Sub
Public Function ContainsKey(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).ContainsKey
CType(m_dictionary, IDictionary(Of TKey, TValue)).ContainsKey(key)
End Function
Public ReadOnly Property Keys() As ICollection(Of TKey) Implements IDictionary(Of TKey, TValue).Keys
Get
Return m_keysInOrder
End Get
End Property
Public Function TryGetValue(ByVal key As TKey, ByRef value As TValue) As Boolean Implements IDictionary(Of TKey, TValue).TryGetValue
Return CType(m_dictionary, IDictionary(Of TKey, TValue)).TryGetValue(key, value)
End Function
Public ReadOnly Property Values() As ICollection(Of TValue) Implements IDictionary(Of TKey, TValue).Values
Get
Dim rtnVal As New List(Of TValue)
For Each key As TKey In m_keysInOrder
rtnVal.Add(m_dictionary(key))
Next
Return rtnVal
End Get
End Property
Public Function GetEnumerator() As IEnumerator(Of KeyValuePair(Of TKey, TValue)) Implements IEnumerable(Of KeyValuePair(Of TKey, TValue)).GetEnumerator
Return New Enumerator(Me)
End Function
Private Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
Return New Enumerator(Me)
End Function
Public Structure Enumerator
Implements IEnumerator(Of KeyValuePair(Of TKey, TValue))
Private m_dictionary As OrderedDictionary(Of TKey, TValue)
Private m_nextIndex As Integer
Private m_current As KeyValuePair(Of TKey, TValue)
Friend Sub New(ByVal orderedDictionary As OrderedDictionary(Of TKey, TValue))
m_dictionary = orderedDictionary
m_nextIndex = 0
m_current = New KeyValuePair(Of TKey, TValue)
End Sub
Public ReadOnly Property Current() As KeyValuePair(Of TKey, TValue) Implements IEnumerator(Of KeyValuePair(Of TKey, TValue)).Current
Get
Return New KeyValuePair(Of TKey, TValue)(m_current.Key, m_current.Value)
End Get
End Property
Private ReadOnly Property Current1() As Object Implements IEnumerator.Current
Get
Return Current
End Get
End Property
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
Dim rtnVal As Boolean = False
If m_nextIndex < m_dictionary.Count Then
rtnVal = True
Dim key As TKey = m_dictionary.m_keysInOrder(m_nextIndex)
m_current = New KeyValuePair(Of TKey, TValue)(key, m_dictionary.Item(key))
m_nextIndex += 1
Else
m_current = New KeyValuePair(Of TKey, TValue)
End If
Return rtnVal
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
m_nextIndex = 0
End Sub
Public Sub Dispose() Implements System.IDisposable.Dispose
End Sub
End Structure
End Class
Hope that helps,
Kevin
Talbot McInnis
I was afraid it would end as something like this, but at least I have another opinion on the subject now so I feel less that I might have missed something, and I can maybe work with changing the design instead, or look into your class. ;) Hm, I may actually end up simply building a List alongside a Dictionary. That kind of overhead doesn't really matter as it's done outside a loop and the memory consumptions won't be big.
I can't really blame MS for this, as Hashtables normally *don't* have well-defined orderings, and only list-style structures have for sure. The problem is more that the code exploits a number of peculiarities with the VB6 design...