tags:

views:

204

answers:

4

Hi,

In VB6, there used to be a Collection data type that would allow retrieval of an item in the collection by either its key or its ordinal. However, it wasn't strongly typed.

Now, with VB.Net, I am looking for a suitable replacement that is strongly type and could be used with a generic collection.

This is a simple example of what I want to do. The only problem is that the underlying collection class, BindingList, does not support efficient retrieval of an item by an alpha key, so I have to loop through the elements to get the item I am looking for. For large collections, this is not efficient.

I have looked though the various Collection type classes and have found no suitable replacement.

This is what I want to do, except for the looping that is done with the Item property.

Rather than just saying "Use Hash tables" or something like that, if you could, please include the detailed out as I have done for the short example below.

Thanks very much, Chad

Public Class Car

    Public Sub New(ByVal keyName As String, ByVal property1 As String)
        _KeyName = keyName
        _Property1 = property1
    End Sub

    Dim _KeyName As String

    Public Property KeyName() As String
        Get
            Return _KeyName
        End Get
        Set(ByVal value As String)
            _KeyName = value
        End Set
    End Property

    Public _Property1 As String

    Public Property Property1() As String
        Get
            Return _Property1
        End Get
        Set(ByVal value As String)
            _Property1 = value
        End Set
    End Property

End Class

Public Class Cars

    Inherits System.ComponentModel.BindingList(Of Car)

    Public Overloads ReadOnly Property Item(ByVal key As String) As Car
        Get
            For Each CurrentCar As Car In Me.Items
                If CurrentCar.KeyName = key Then
                    Return CurrentCar
                End If
            Next
            Return Nothing
        End Get
    End Property

End Class
+3  A: 

I believe you're looking for Dictionary<TKey, TValue>. In fact, if you do want your own collection class that's strongly typed and isn't (itself) generic, if you change your parent class to Dictionary<string, Car>, you should be all set. This all does, of course, assume that you add the cars to the collection with an explicit string key. If you want the lookup to be based on the value of a property in the collection, you'd do better either using or inheriting from List<Car> and using LINQ to query the list. You could then have...

Public Overloads ReadOnly Property Item(ByVal key As String) As Car
    Get
        Return (from c in Me where c.KeyName = key select c).SingleOrDefault()
    End Get
End Property
Adam Robinson
A: 

Do you really need both access by key AND index?

If you do not, then use a Dictionary(Of String, Car), and use - MyCol.Items("XXX") to retrieve an item by key (or the shorthand MyCol("XXX")) - MyCol.ContainsKey("XXX") to test if a key exists in the collection - For Each Entry as KeyValuePair(Of String, Car) in MyCol if you want to enumerate all objects AND their key - For Each Entry as Car in MyCol.Values if you want to enumerate the entries without consideration for the key

If you need both access by index and key, I'm afraid your best bet is to use a List(of Car) and a Dictionary(of Car) rolled into one custom collection class, because I believe they went away from that kind of collection which is not really all that useful for most problems.

Denis Troller
Yep, it looks like the Dictionary object will do it just fine. I guess I didn't realize that the elements within it could be typed
Velika
A: 

This is what I am thinking is my best solution. I welcome comments for a better way!

Imports Microsoft.VisualBasic



Public Class Car

    Implements Xs(Of Car).IKeyName

    Private _KeyName As String

    Public Sub New(ByVal keyName As String, ByVal property1 As String)
        _KeyName = keyName
        _Property1 = property1
    End Sub

    Public Property KeyName() As String Implements Xs(Of Car).IKeyName.KeyName
        Get
            Return _KeyName
        End Get
        Set(ByVal value As String)
            _KeyName = value
        End Set
    End Property

    Public _Property1 As String

    Public Property Property1() As String
        Get
            Return _Property1
        End Get
        Set(ByVal value As String)
            _Property1 = value
        End Set
    End Property

End Class

Public Class Cars

    Inherits System.ComponentModel.BindingList(Of Car)

    Public Overloads ReadOnly Property Item(ByVal key As String) As Car
        Get
            For Each CurrentCar As Car In Me.Items
                If CurrentCar.KeyName = key Then
                    Return CurrentCar
                End If
            Next
            Return Nothing
        End Get
    End Property

End Class

Public Class X

    Private _KeyName As String

    Public Property KeyName() As String
        Get
            Return _Keyname
        End Get
        Set(ByVal value As String)
            _Keyname = value
        End Set
    End Property
End Class

Public Class Xs(Of X)

    Inherits Hashtable

    Interface IKeyName
        Property KeyName() As String
    End Interface

    Public Shadows Sub Add(ByVal item As IKeyName)
        MyBase.Add(item.KeyName, item)
    End Sub

    Public Shadows ReadOnly Property Item(ByVal key As String) As x

        Get
            If Me.ContainsKey(key) Then
                Return MyBase.Item(key)
            Else
                'If I mispell a key, I don't want to end up creating a new mispelled version, I want an error
                Throw New Exception("Element with key " & key & " is not found")
            End If
        End Get
    End Property

End Class

Public Class Cars2

    Inherits Xs(Of Car)

End Class

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim MyCar As New Car("key1", "prop1")

        'First approach
        Dim MyCars As New Cars

        MyCars.Add(MyCar)

        Dim RetrievedCar As Car = MyCars.Item("key1") 'Inefficient retrieval by key (uses looping)

        'Second approach
        Dim Cars2 As New Cars2

        Cars2.Add(MyCar)

        Dim RetrievedCar2 As Car = Cars2.Item("key1") 'Can now efficiently retreive an item by its key

End Sub
Velika
You're now creating your own non-generic Dictionary when there already is one for you in Dictionary<TKey, TValue>. Why?
Adam Robinson
A: 

The OrderedDictionary in the System.Collections.Specialized namespace can be accessed by index and by key, if you ever need that. But looking at your solution, it looks like a standard Dictionary, but less efficient because it forces a string type for keys.

Is there any reason you can't use the Dictionary .NET provides you, or another collection type that's already in .NET like OrderedDictionary?

Meta-Knight