Using Dictionary With Interfaces

JoeBuntu

Member
Joined
Jun 20, 2009
Messages
8
Programming Experience
1-3
What I would like to do is make an abstract class that will hold objects in a dictionary and manage the adding/deleting/key changes of those items in the dictionary. My problem is that in order to make the class as generic as possible I declared the dictionary as dictionary(of long, of SomeInterface).

The problem is when I try to pass a strongly typed dictionary in the constructor using dictionary( of long, ClassThatImplementsSomeInterface) I get a compiler error because it cannot convert to that type.

The reason I want to pass the dictionary in the abstract class's constructor is so that I can use the strongly typed dictionary in the subclass and have the abstract class manage the keys when it needs to. So how can I do something like this? Also- If there is a much better way to manage keys than what I am doing feel free to let me know, but I am much more interested in how to make this work since I have encountered this type of problem a few times before. Thanks alot.

Here's the abstract class:
VB.NET:
Public MustInherit Class HasChildrenKeys

    Protected m_childItems As Dictionary(Of Long, IHasKey)

    Public Sub New(ByVal childItemsDict As Dictionary(Of Long, IHasKey))
        m_childItems = childItemsDict

    End Sub
    Public Sub AddItem(ByVal item As IHasKey)
        AddHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Add(item.Key, item)
    End Sub
    Public Sub RemoveItem(ByVal item As IHasKey)
        RemoveHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Remove(item.Key)
    End Sub
    Private Sub ChangeKey(ByVal e As KeyChangeEventArgs)
        If m_childItems.ContainsKey(e.OldKey) Then
            Dim item = m_childItems.Item(e.OldKey)
            Me.RemoveItem(item)
            item.Key = e.NewKey
            Me.AddItem(item)
        End If
    End Sub
End Class

This is how I am trying to use the subclass
VB.NET:
Public Class TestClass
    Inherits HasChildrenKeys

    'BomDetailItem implements IHasKey
    Private m_detailItems As New Dictionary(Of Long, BomDetailItem)

    Sub New()
        'this does not work
        'Error: Dictionary(Of Long, BomDetailItem) cannot be converted to 
        'Dictionary(Of Long, IHasKey)
        MyBase.New(m_detailItems)
    End Sub
End Class
 
You can create instance of Dictionary(Of Long, IHasKey), which means you can put BomDetailItem values in it.

More beneficial would probably be to define the base class as a generic class. F.ex this generic class has the limitiation that the type T must implement IHasKey:
VB.NET:
Public MustInherit Class HasChildrenKeys(Of T As IHasKey)

    Protected m_childItems As Dictionary(Of Long, T)

    Public Sub New(ByVal childItemsDict As Dictionary(Of Long, T))
        m_childItems = childItemsDict
    End Sub
    Public Sub AddItem(ByVal item As T)
        AddHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Add(item.Key, item)
    End Sub
    Public Sub RemoveItem(ByVal item As T)
        RemoveHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Remove(item.Key)
    End Sub
    Private Sub ChangeKey(ByVal e As KeyChangeEventArgs)
        If m_childItems.ContainsKey(e.OldKey) Then
            Dim item = m_childItems.Item(e.OldKey)
            Me.RemoveItem(item)
            item.Key = e.NewKey
            Me.AddItem(item)
        End If
    End Sub
End Class
VB.NET:
Public Class TestClass
    Inherits HasChildrenKeys(Of BomDetailItem)
    'BomDetailItem implements IHasKey

    Sub New()
        MyBase.New(New Dictionary(Of Long, BomDetailItem))
    End Sub
End Class
The list instance can also be create in the base class, only from the type parameter supplied.
 
Thanks

Thanks so much, I've been stuck at this wall for many other ideas of mine as well. That works perfect. Does this technique have an official name?
 
The list instance can also be created in the base class, only from the type parameter supplied.
Thought I'd give an example of this also:
VB.NET:
Dim typeargs() As Type = {GetType(Long), GetType(T)}
Dim listtype As Type = GetType(Dictionary(Of ,)).MakeGenericType(typeargs)
m_childItems = CType(Activator.CreateInstance(listtype), Dictionary(Of Long, T))
Of course the minute I posted this I figured there is also a much simpler solution, in base class you can just use the New constructor directly, when the type argument is specified the correct instance type is created:
VB.NET:
Protected m_childItems As [B]New [/B]Dictionary(Of Long, T)
 
How about generics that use other generics?

Thanks for your help so far- I started rearranging my class structures to take advantage of generics. I ran into the following problem which I could not figure out.

In the sample below I am trying to take an interface that uses generics and use that interface as the generic of another interface.

If you look at the abstract class I want to make that implements the IHasChildrenKeys Interface- Ultimately what I am trying to acheive is to be able declare a dictionary using the generic type used in the IHasKey interface as the key type instead of hard coding the type as Long

So how is there a way for me to use the IHasKey interface as the generic type without specifying the generic type of IHasKeys? And is there a way to access the generic type used so that I may be able to declare my dictionary with the generic type chosen for IHasKey as the type used as the key for the dictionary?

I hope that makes sense!

VB.NET:
Public Interface IHasKey(Of T)
    Property Key() As T
    Event KeyChanged(ByVal e As KeyChangeEventArgs)
End Interface

Interface IHasChildrenKeys(Of SomeClass As IHasKey)
    'This doesn't work- the compiler requires me to specify the
    'Type used by IHasKey like So:
    'Interface IhasChildrenKeys(of someInterface As IHasKey(Of <type>)

    Sub AddItem(ByVal item As SomeClass)
    Sub RemoveItem(ByVal item As SomeClass)
    Sub ChangeKey(ByVal e As KeyChangeEventArgs)
    Function ItemsToArray() As Array
    Function ItemsToList() As List(Of SomeClass)
End Interface

'Here would be an example of how I would like to implement the IHasChildrenKeys Interface:

Public MustInherit Class HasChildrenKeys(Of T As IHasKey)
    Implements IHasChildrenKeys(Of T)

    'Instead of using Long, I would like to use a whatever generic type
    'is used in IhasKey(Of T)
    Protected m_childItems As Dictionary(Of Long, T)

    Public Sub New(ByVal childItemsDict As Dictionary(Of Long, T))
        m_childItems = childItemsDict

    End Sub
    Public Sub AddItem(ByVal item As IHasKey) Implements IHasChildrenKeys(Of T).AddItem
        AddHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Add(item.Key, item)
    End Sub
    Public Sub RemoveItem(ByVal item As IHasKey) Implements IHasChildrenKeys(Of T).RemoveItem
        RemoveHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Remove(item.Key)
    End Sub
    Private Sub ChangeKey(ByVal e As KeyChangeEventArgs) Implements IHasChildrenKeys(Of T).ChangeKey
        If m_childItems.ContainsKey(e.OldKey) Then
            Dim item = m_childItems.Item(e.OldKey)
            Me.RemoveItem(item)
            item.Key = e.NewKey
            Me.AddItem(item)
        End If
    End Sub
    Public Function ItemsToArray() As Array Implements IHasChildrenKeys(Of T).ItemsToArray
        Return m_childItems.Values.ToArray
    End Function
    Public Function ItemsToList() As List(Of T) Implements IHasChildrenKeys(Of T).ItemsToList
        Return m_childItems.Values.ToList
    End Function
End Class
 
This works but, a better way would be... Better

I found a way to do what I want to do, except it requires using another parameter to constrain the generic type of the IHasKey. If there is a way to do this (that is still type safe) without having to use two parameters, that would be nice.

VB.NET:
Public Interface IHasKey(Of T)
    Property Key() As T
    Property KeyType() As System.Type
    Event KeyChanged(ByVal e As KeyChangeEventArgs(Of T))
End Interface

Interface IHasChildrenKeys(Of KeyType, SomeClass As IHasKey(Of KeyType))

    Sub AddItem(ByVal item As SomeClass)
    Sub RemoveItem(ByVal item As SomeClass)
    Sub ChangeKey(ByVal e As KeyChangeEventArgs(Of KeyType))
    Function ItemsToArray() As Array
    Function ItemsToList() As List(Of SomeClass)
End Interface

Public MustInherit Class HasChildrenKeys(Of KeyType, T As IHasKey(Of KeyType))
    Implements IHasChildrenKeys(Of KeyType, T)

    Protected m_childItems As Dictionary(Of KeyType, T)

    Public Sub New(ByVal childItemsDict As Dictionary(Of KeyType, T))
        m_childItems = childItemsDict

    End Sub
    Public Sub AddItem(ByVal item As T) Implements IHasChildrenKeys(Of KeyType, T).AddItem
        AddHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Add(item.Key, item)
    End Sub
    Public Sub RemoveItem(ByVal item As T) Implements IHasChildrenKeys(Of KeyType, T).RemoveItem
        RemoveHandler item.KeyChanged, AddressOf ChangeKey
        m_childItems.Remove(item.Key)
    End Sub
    Private Sub ChangeKey(ByVal e As KeyChangeEventArgs(Of KeyType)) Implements IHasChildrenKeys(Of KeyType, T).ChangeKey
        If m_childItems.ContainsKey(e.OldKey) Then
            Dim item = m_childItems.Item(e.OldKey)
            Me.RemoveItem(item)
            item.Key = e.NewKey
            Me.AddItem(item)
        End If
    End Sub
    Public Function ItemsToArray() As Array Implements IHasChildrenKeys(Of KeyType, T).ItemsToArray
        Return m_childItems.Values.ToArray
    End Function
    Public Function ItemsToList() As List(Of T) Implements IHasChildrenKeys(Of KeyType, T).ItemsToList
        Return m_childItems.Values.ToList
    End Function
End Class
 
For clarity lets define the interface with these generic names:
VB.NET:
Interface IHasChildrenKeys(Of Tkey, Tvalue As IHasKey(Of Tkey))
I don't see how what you ask would be possible, IHasChildrenKeys requires two different types (Tkey and Tvalue), HasChildrenKeys must specify both these types so the types must be known or generic. It is the Of list that specifies this type list, and if you omit "Of Tkey" then the generic "Tkey" also can't be used to specify the Tvalue type because it is simply not known.
 
Back
Top