Collections in VB.NET

If you didn’t get the picture already, the .NET Framework is huge.  It is an extremely adept and elegant framework designed by one of my industry heroes, Anders Hejlsberg.  However there is no getting away from it, this thing is huge.  In today’s tutorial we’re going to delve into of the most important 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 Namespaces as they are affectionately known).  The Collection Classes in this Namespace are primarily abstract classes for you to inherit/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/purposes and include:

Name

Description

Dictionary

A simple fast collection of keys and value pairs.

HashSet

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.

This is just one Namespace in a vast library of Namespaces.  See what I mean when I said this Framework is huge?  Each Collection has it’s own characteristics designed to solve different problems.  If we look at our two BookCollection classes, we aren’t using a generic .NET class.   The Visual Basic Collection Class was only made available to VB developers as a means of backwards compatibility with Visual Basic 6’s.  It was solely developed to reduce the effort involved in 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 generic .NET classes to choose from.

In the previous tutorial we’d been working with the VB6 collection class.  This class is abstracted from the presentation layer in such a way that we can swap it with any other .NET collection class with no impact on our form.  In this tutorial, we are going to enhance our own Collection Class project, VB Collection Class for a more generic .NET Collection Class.

If we look at our Book Collections, they provide consumers with the ability to:

  • Add items to the class
  • Remove items from the class
  • Count the number of items in the collection
  • Retrieve individual items
  • Retrieve individual keys

These are represented in our Add and Remove methods as well as the Item, Key and Count properties. We also have a degree of backwards compatibility issues to worry about as the syntax of these items is set in stone, we have a Form calling these methods and properties and changing the method signatures will cause this form to break!

Looking closely at the .NET Dictionary object, it has an Item and Count property as well as the Add and Remove methods we need. Unfortunately it doesn’t allow access to items by Index and Key.  There is a specialised class that allows this stored under System.Collections.Specialized Namespace 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

In the AddItem method, we’ve used the the Insert method of the OrderedDictionary class to add items to the collection.  The Insert method allows us to add items in certain locations, which is exactly what we need in this routine to add the item in the correct alphabetic order.

We’ve now removed all references to colISBN.  So we’re good to go.  Right?

Unfortunately, Microsoft changed collection indexing when they introduced VB.NET  In VB.NET collections are indexed from 0 now as opposed to 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 super-dooper .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? Let me know what you would change and why.  Answers on a postcard to the usual place.