Collections in VB.NET

If you didn’t get the picture already, the .NET Framework is huge.  Is is an extremely adept and elegant framework designed by one of my heroes in the industry Anders Hejlsberg.  However there is no getting away from it, this thing is huge.  In today’s tutorial we’re going to delve into one much used set of libraries and look at Collections in VB.NET.

Collections in VB.NET

Out of the box, we have a number of Collections in the System.Collections library (or namespace).  These are primarilly abstract classes for you to inherit and extend and are listed in the Microsoft MSDN (Microsoft Developer Network) site, which is a great and essential resource for any would be VB developers.

However if you’d like to work with a set of concrete collections, rather than the abstract kind, the System.Collections.Generic namespace contains the following collections for you to work with right away.

These collections all have different characteristics and purposes and include:

Name

Description

Dictionary

A simple fast collection of keys and value pairs

HashSet

Provides a very fast low memory collection of values

KeyedByTypeCollection

Provides a collection whose items are types that serve as keys.

LinkedList

A fast linked list allowing the items to be rearranged quickly.

List

A strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.

Queue

A first-in, first-out collection of objects.

SortedDictionary

A Dictionary sorted by key.

SortedList

A List sorted by key.

SortedSet

A collection of objects that is maintained in sorted order.

Stack

Represents a variable size last-in-first-out (LIFO) collection of instances of the same type.

See what I mean?  This Framework is huge.  Each collection has it’s own attributes and solves different problems.  If we look at our two BookCollection classes, we aren’t using a generic .NET class but a Collection Class derived from a .NET class.   The Collection Class is really available to VB developers as a means of backwards compatibility with Visual Basic 6′s own collection class.  It was designed to east the pain of migrating your app from VB6 to the brave new world of .NET.  Going forwards it isn’t recommended to use this class at all as there are a wide range of classes to choose from.

In the previous tutorial we’d been working with the VB6 collection class, but have abstracted it away from the presentation layer in such a way, we can swap it with any of the recommended .NET collection classes with no impact on class consumers such as our form.  In this tutorial, we are going to enhance our own Collection Class project, swapping useage of the VB Collection Class for a more generic .NET Collection Class.

If we look at our collections for managing books, we wanted to search the class as well as sort the class.  We also needed to ability to retrieve both keys and values.  We also have a degree of backwards compatibility ourselves to worry about as we are calling these methods in our presentation layer; Add, Count and Remove method as well as an Item property.

Looking at the .NET Dictionary object, it has an Item property,  as well as the other methods we need!  Unfortunately you can’t access a Dictionary object by Index and Key.  There is a specialised class to do this stored under System.Collections.Specialized called the OrderedDictionary class.  The purpose of this class is, and I quote, to represent a collection of key/value pairs that are accessible by the key or index.

This is just the class we need!
Open the Collection Class project and add this first line so the BookCollectionBase looks as follows

Imports System.Collections.Specialized

Public MustInherit Class BookCollectionBase

The Imports statement tells the Framework that we will be using classes in this library, or namespace as they are referred to in the world of .NET.

Change our definition of colBookNames to

Private colBookNames as OrderedDictionary = New OrderedDictionary

Also, delete the declaration for colISBN as the Dictionary already allows access to both keys and values.  We don’t need to maintain two collections ourselves.

If we didn’t have the Imports statement our declaration for colBookNames would be

Private colBookNames As New System.Collections.Specialized.OrderedDictionary

This works but is very unwieldy to type.  If we needed to use two classes in this namespace then it’s a downright pain.  Remember Do Not Repeat Yourself.  Imports is a useful means of keeping our code readable.

In the Remove method delete the line referencing colISBN so it looks like

Public Sub Remove(ByVal isbn As String) Implements IBookCollection.Remove
     colBookNames.Remove(isbn)
End Sub

Change the Key method to look as follows, removing the reference to colISBN.

Public Function Key(ByVal index As Integer) As String Implements IBookCollection.Key
     Return colBookNames.Keys(index)
End Function

Also change the AddItem routine to the following:

' Add an item at its correct position.
Protected Sub AddItem(ByVal item As String, ByVal key As String)

     Dim i As Integer

     ' See where the item belongs.  Iterate through the entire list
     For i = 0 To colBookNames.Count - 1
          If colBookNames.Item(i) >= item Then
               Exit For
          End If
     Next i

     ' Insert the item.
     If i > colBookNames.Count Then
          ' Add at the end.
          colBookNames.Insert(colBookNames.Count(), key, item)
     Else
          ' Add at the right position.
          colBookNames.Insert(i, key, item)
     End If
End Sub

We’ve removed all references to colISBN.  In addition, we’ve used the the Insert method of the OrderedDictionary.  The Insert method allows us to add items in certain locations, which is exactly what we need in this routine, adding the item in the correct alphabetic order.

Unfortunately, Microsoft chnaged how collections are indexed from VB6 to VB.NET.  In VB.NET collections are indexed from 0 now as opposed to the 1 in VB6.     The moral of this story is always check the lower bounds of any collection.  Is it 0, or is it 1.  They change.

This change of indexing forces us to make a change to the Presentation Layer.  The Form routine AddBooksToListBox must iterate from 0 instead of 1 to Count -1 instead of Count.  now.  Replace the code with this.

Private Sub AddBooksToListBox()

     Dim i As Integer

     For i = 0 To BookCollection.Count - 1
          ListBoxBooks.Items.Add(BookCollection.Key(i) & " - " & BookCollection.Item(i))
     Next
End Sub

In this tutorial we’ve re-architected the solution to use a new upgraded superdouper .NET collection.  We evaluated what was available, chosen what we believe is the best solution to the problem in our circumstances and made the changes.  This change did have some forced impact on the presentation layer (indexing from 0 instead of 1) but we understood the impact and the impact was small.

VB.NET Collection Class Archtecture

The beauty of having a tiered architecture is that the vast majority of the work was in the Base Class.  We had a minor change to make in the Presentation Layer but all other layers remain untouched.   This shows the power of obeying the Single Responsibility principle.  To make a change to the tool we use to manage collections meant we only needed to change the single area responsible for managing that tool.

Welcome to the world of developing clean code and welcome to the .NET Framework!

Question, if you were to re-architect another area of this application, what would you do next.  Articulate where you would change, what you would change and why.  Answers on a postcard to the usual place.