AddThis Social Bookmark Button

Print

New Features in VB.NET — Generics

by Wei-Meng Lee
05/25/2004

One of the new features in .NET Framework 2.0 is the support of Generics in the Intermediate Language (IL). As such, languages such as C# and VB.NET now support this new feature. You've heard a lot about Generics in C#, but seldom hear people talk about it in VB.NET. (Note that Generics is a feature of the IL, and not specific to C# alone). And so in this article I'm going to introduce Generics to the VB.NET programmer.

Understanding Generics

To understand how Generics work, let's consider an example. Suppose you need to implement a Stack class. A stack is a Last-In-First-Out (LIFO) data structure that allows you to push items into the stack, as well as pop items out of a stack. One possible implementation is:

Public Class MyStack
  Private element() As Integer ' create a dynamic array
  Private pointer As Integer

  Public Sub New(ByVal size As Integer)
    ReDim element(size - 1)
    pointer = 0
  End Sub

  Public Sub Push(ByVal item As Integer)
    If pointer > UBound(element) Then
      Throw New Exception("Stack is full.")
    End If
    element(pointer) = item
    pointer += 1
  End Sub

  Public Function Pop() As Integer
    pointer -= 1
    If pointer < 0 Then
      Throw New Exception("Stack is empty.")
    End If
    Return element(pointer)
  End Function
End Class

As you can see, this Stack implementation accepts stack items of the Integer data type. If you wish to use this implementation for another data type, say String, you need to create another identical class but instead use the String type. Obviously this is not a very flexible way of writing your class definition.

One common way of solving this problem is to use the Object data type so that the compiler will use late-binding (note the changes in bold):

Public Class MyStack
  Private element() As Object ' uses Object for late-binding
  Private pointer As Integer
  Public Sub New(ByVal size As Integer)
    ReDim element(size - 1)
    pointer = 0
  End Sub
  Public Sub Push(ByVal item As Object) 
    If pointer > UBound(element) Then
      Throw New Exception("Stack is full.")
    End If
    element(pointer) = item
    pointer += 1
  End Sub

  Public Function Pop() As Object
    pointer -= 1
    If pointer < 0 Then
      Throw New Exception("Stack is empty.")
    End If
    Return element(pointer)
  End Function
End Class

One problem with this approach is that when you use the Stack class, you may inadvertently "push" in the wrong data type, as shown in the following code in bold:

Dim stackC As New MyStack(2)
stackC.Push(5)
stackC.Push("A")
Dim val1, val2 As Integer
val1 = stackC.pop   'runtime error here

However, this mistake will only be detected during runtime when you try to pop out a String value and try to assign it to an Integer variable.

In VB.NET, a new type known as Generics is supported. Using Generics, you do not need to fix the data type of the items used by your Stack class. Instead, you use the new keyword Of, which identifies the data type parameter on a class, structure, interface, delegate, or procedure. To see the use of the Generics, let's rewrite the Stack class:

Public Class MyStack(Of itemType)
  Private element() As itemType ' create a dynamic array
  Private pointer As Integer

  Public Sub New(ByVal size As Integer)
    ReDim element(size - 1)
    pointer = 0
  End Sub

  Public Sub Push(ByVal item As itemType) 
    If pointer > UBound(element) Then
      Throw New Exception("Stack is full.")
    End If
    element(pointer) = item
    pointer += 1
  End Sub

  Public Function Pop() As itemType
    pointer -= 1
    If pointer < 0 Then
      Throw New Exception("Stack is empty.")
    End If
    Return element(pointer)
  End Function
End Class

As highlighted in bold, I have used the name itemType as a placeholder for the eventual data type that I want to use for your class. That is, during the design stage of this class, I don't specify the data type that my Stack class will deal with.

If I want my Stack class to manipulate items of type Integer, I specify that during the instantiation stage:

Dim stackA As New MyStack(Of Integer)(2) 
' the itemType will now be replaced by the Integer
stackA.Push(5)
stackA.Push(6)
stackA.Push("Some string...") ' compile-time error
MsgBox(stackA.pop)
MsgBox(stackA.pop)
MsgBox(stackA.pop)

The third Push() method will generate a compile-time error. This is because the compiler checks the data type used by my Stack class during compile time. This is one advantage of using Generics in VB.NET.

If I want to use the Stack class for String data types, I simply do this:

Dim stackB As New MyStack(Of String)(3) 
stackB.Push("Generics")
stackB.Push("supports ")
stackB.Push("VB.NET ")
MsgBox(stackB.pop)
MsgBox(stackB.pop)
MsgBox(stackB.pop)

Advantages of Generics

Based on what I've discussed in the previous section, it's not difficult to see the following advantages of using Generics:

  • Type Safety -- Generic types enforce type compliance at compile-time, and not run-time (as in the case of using Object). This reduces the chances of data-type conflict during run-time.
  • Performance -- The data types to be used in a Generic class are determined at compile-time, hence there is no need to perform type casting during run-time, which is a computationally costly process.
  • Code reuse -- Since you only need to write the class once and customize it to use with the various data types, there is a substantial amount of code-reuse.

Terms

Figure 1 shows the terms used in a Generic type:


Figure 1. Terms used in Generics.

Using Constraints in a Generic Type

You can further impose constraints on a Generic type. For example, if I want my Stack class to only manipulate objects of a certain type, say of type Employee, I can declare my Generic type as:

Public Class MyStack(Of itemType As Employee)

You can also specify multiple constraints in a Generic type. The syntax for specifying multiple constraints is {type1, type2, ..., typeN}. As an example, if I want the Stack class to manipulate objects of type Employee and also implement the Icompatable interface, I can declare my Generic type as:

Public Class MyStack(Of itemType As {Employee, IComparable})

To see the use of constraints in Generics, assume that I have the following class definitions:

Public Class Employee
  Implements IComparable
  Public employeeID As String
  Public name As String
  Public Function CompareTo(ByVal obj As Object) As Integer _
   Implements System.IComparable.CompareTo
    ' add code here
  End Function
End Class

Public Class Manager
  Inherits Employee
  ...
End Class

Public Class Programmer
  Inherits Employee
  ...
End Class

Then the following statements are valid:

Dim stackD As New MyStack(Of Programmer)(3)
Dim stackE As New MyStack(Of Employee)(3)
Dim stackF As New MyStack(Of Manager)(3)

Summary

Generics is an improved way to write your classes. And by doing so, your code is now much simpler to understand, more efficient, and most importantly, safer.

Wei-Meng Lee (Microsoft MVP) http://weimenglee.blogspot.com is a technologist and founder of Developer Learning Solutions http://www.developerlearningsolutions.com, a technology company specializing in hands-on training on the latest Microsoft technologies.


Return to ONDotnet.com.