VB.NET: Improved multidimensional hashtable
09 Jul 2007
in the early morning
Matt Winckler
(Update: see also the C# version.)
More than a month ago I posted about implementing multidimensional hashtables in VB.NET by using Generics and descending a class from Hashtable. It had some limitations, however, including the fact that String wouldn’t work nicely as a value type in it. Well, the next day (or the day after) I figured out a different way to do things and never got around to posting it…until now. Allow me to introduce: DynamicDictionary.
''' <summary>
''' This class is identical to Dictionary except that
''' when you attempt to access a key that does not exist,
''' it will automatically populate that key with a newly-
''' created object of the appropriate type (v).
''' </summary>
''' <typeparam name="k">A Type to use as the key.</typeparam>
''' <typeparam name="v">
''' A Type to use as the value.
''' This Type must have an accessible parameterless
''' constructor.
''' </typeparam>
''' <remarks></remarks>
Public Class DynamicDictionary(Of k, v As New)
Inherits Dictionary(Of k, v)
Default Public Shadows Property Item(ByVal key As k) As v
Get
Dim ret As v = Nothing
' If the requested bucket is a null reference,
' create a new instance of whatever type
' belongs here: a new v.
If (Not MyBase.TryGetValue(key, ret)) Then
ret = New v()
MyBase.Add(key, ret)
End If
Return ret
End Get
Set(ByVal value As v)
MyBase.Item(key) = value
End Set
End Property
End Class
This class prevents the (seriously lame) default behavior of .NET’s Dictionary, which is to throw an exception when attempting to access a null bucket (instead of just returning the null reference, as a Hashtable would). Instead, it instantiates a new object of the appropriate type and returns that. To declare a three-dimensional “hashtable”, then, would look like this:
Dim myDictionary as New DynamicDictionary(Of Integer, _
DynamicDictionary(Of String, Double))
myDictionary(4)("key1") = 7.51
This new class solves a couple of problems the old one had. One such problem is that when using Hashtable, boxing/unboxing could still occur on value types. By descending from the new generic built-in Dictionary class, this new class should never do any boxing.
One issue that still remains is dealing with String, which has no accessible parameterless constructor and therefore cannot be used as the value argument of a DynamicDictionary. The only solution I’ve found in that case is to use a normal Dictionary instead:
Dim myDictionary as new DynamicDictionary(of Double, _
Dictionary(of Integer, String))
myDictionary(7.51)(4) = "something"
The drawback, of course, is that it will behave like a Dictionary and throw exceptions if you access a null bucket:
Dim myNullReference as String = myDictionary(999)(125) ' Throws exception!
If desired, you could get around this by creating a new class (”SafeDictionary”?) and using the TryGetValue() method I used above and instead of creating a new instance, simply return the null reference. (You could then also remove the New constraint from the class definition.)
To get true Hashtable behavior and return null references instead of new objects, I overloaded the Item property on DynamicDictionary to allow you to specify whether a new instance should be created.
Default Public Shadows Property Item(ByVal key As k, ByVal AutoCreateItem As Boolean) As v
Get
Dim ret As v = Nothing
If (AutoCreateItem) Then
ret = Item(key)
Else
MyBase.TryGetValue(key, ret)
End If
Return ret
End Get
Set(ByVal value As v)
MyBase.Item(key) = value
End Set
End Property

wow! so little code, such a huge impact!
thanks very much, very easy to use what you put together.
THANKS. GREAT AND SMART SOLUTION.
I made a little change ad try to mimic the real “Nothing” situation. I need the “nothing”. Just my 2 cents.
Public Class MyHashTable(Of k, v As New)
Inherits Dictionary(Of k, v)
End Class
Public Class MyNestedHashTable(Of k, v As New)
Inherits MyHashTable(Of k, v)
End Class
Usage:
Dim data_ As MyHashTable(Of String, MyNestedHashTable(Of String, Object)) = New MyHashTable(Of String, MyNestedHashTable(Of String, Object))()
data_.Item(”X”)(”Y”) = 123
data_.Item(”Z”)(”Y”) = 12346
MsgBox(data_.Item(”X”)(”Y”) + data_.Item(”Z”)(”Y”))
Dim obj As Object = data_.Item(”W”)(”Z”, True)
If obj Is Nothing Then
MsgBox(”Nothing Yet”)
End If