.NET Generics for VB Programmers

In The C++ Programming Language, Bjarne Stroustrop discusses the pseudo-invention of template methods in the C programming language by employing the preprocessor and macros. Stroustrop builds on this clever use of C's preprocessor and macro capability, making templates a full-fledged part of the fledgling C with Classes (now C++). The added advantage is that templates are type checked by the compiler rather than simple text substitution by a preprocessor.

.NET 2.0 supports templates called generics. The basic concept is the same: Define a method or a class with one or more methods, specifying the data type as a replaceable element. By convention, the capital letter T is used. Finally, when you use the method or class, indicate a type for the generic T and the compiler will use that new information to generate a new, unique method or class based on that type. The new element becomes a complete and distinct block of code that benefits from being processed by the compiler.

This article shows how to define generic methods and classes and use the generic classes that ship with .NET 2.0.

Defining Generic Methods

The generics capability of .NET 2.0 can be defined at the granularity of a single method. The key is to separate data type from algorithm and parameterize the data type. Each reference to the method with a distinct data type yields a distinct method. Generic methods also support constraints and overloading. A generic method constraint is a new language feature that adds a type constraint to the generic type; this predicate restricts the data type of the generic parameter. Consequently, methods can be overloaded based on the presence and absence of generic parameters. (An upcoming section reviews a few examples of overloaded generic and non-generic methods.)

The classic use of generics is separating data types from common algorithms, the canonical example being turning sort algorithms into generic sorting algorithms. A straightforward example is parameterizing a Swap method. Listing 1 demonstrates how you can define a generic Swap method that swaps any two reference arguments. It includes a console application that demonstrates how to invoke the generic method:

Listing 1: How to Invoke the Generic Method

Module Module1

  Sub Main()
      Dim I As Integer = 5
      Dim J As Integer = 7
      Swap(Of Integer)(I, J)
      Console.WriteLine("I = " & I)
      Console.WriteLine("J = " & J)

      Dim S As String = "Paul"
      Dim R As String = "Lori"
      Swap(Of String)(S, R)
      Console.WriteLine("S = " & S)
      Console.WriteLine("R = " & R)

      Console.ReadLine()

  End Sub

    Public Sub Swap(Of T)(ByRef a As T, ByRef b As T)

      Dim temp As T
      temp = a
      a = b
      b = temp
    End Sub

End Module
Note: In this instance, you also could use an object data type for the Swap method because all .NET types share a common base type.

The new elements needed to define a generic method are the parentheses, the Of keyword, and an argument representing the generic type, as demonstrated by the (Of T) characters after the method name Swap. Then, everywhere the generic type is used, use the parameterized argument (in this example, T).

Swap is a generic method that has two replaceable arguments and one replaceable local variable. For example, Swap(Of Integer) effectively results in a Swap method with two Integer parameters and a temp variable defined as an Integer.

Adding a Generic Method Constraint

Suppose you want to constrain types in Swap (in Listing 1) to non-nullable structure types. You could add a constraint to Swap to indicate that Swap is applicable only to value types (structures). Listing 2 shows Swap defined as being constrained to structures (or value types). The result is that Swap(Of String) in Listing 1 will no longer work:

Listing 2: Swap with a Constraint Limiting the Swap Method to Value Types

Public Sub Swap(Of T As Structure)(ByRef a As T, ByRef b As T)
  Dim temp As T
  temp = a
  a = b
  b = temp
End Sub

Parameterized types can be limited to structures, classes, base classes, interfaces, and types that have a default constructor (Sub New with no parameters). The As predicate in bold in Listing 2 demonstrates how to constrain the parameterized type.

Generics support defining multiple parameterized types, and each parameterized type can have no or multiple constraints.

Overloading Generic Methods

Methods can be overloaded based on arguments but not return types, and methods can be overloaded by parameterized types too. For example, all of the following can exist in the same scope:

Sub Foo
Sub Foo(ByVal s As String)
Sub Foo(Of T)( ByVal arg As T)
Sub Foo(Of T, U)(ByVal arg1 As T, ByVal arg2 as U)

.NET Generics for VB Programmers

Defining a Generic Class

Generic classes are defined in the same instances that regular classes are defined, with one difference. A regular class is defined when you have data and more then one method that work as a cohesive unit or solution. Generic classes are defined when methods and data work as a cohesive unit and it is possible to abstract the data in such a way that multiple data types could be supported with the same code. For example, queues, lists, and stacks do not care what they store, just how they store it. If you use a queue, stack, or list of objects, you have to perform messy type conversions all over your code. If you use a generic queue, stack, or list, the type conversion occurs internally to the class. That is to say, the locus for the messy type conversions converge to an internal point in the class, and the class consumer can rely on compiler-checked types and isn't required to perform if-conditional checks and type conversions.

Defining a generic class is like defining multiple generic methods, with one addition: the (Of T) construct is used in the class header too. To demonstrate, you have a defined a generic strongly typed collection (see Listing 3) derived from System.Collections.CollectionBase. You can use this one class for any data type, as if you had defined a custom typed collection for all types:

Listing 3: A Generic Strongly Typed Collection

Module Module1
    Sub Main()
      Dim BrokenBones As TypedCollection(Of OrthoInjury) = _
        New TypedCollection(Of OrthoInjury)

      BrokenBones.Add(New OrthoInjury(True, _
        "Broken Right Clavicle", "Vicodin; Heals n 8 to 12 weeks"))
      BrokenBones.Add(New OrthoInjury(True, _
        "Fractured Posterior Rib #5", "Heals in 6 to 8 weeks"))

      BrokenBones.Add(New OrthoInjury(True, _
        "Fractured Posterior Rib #1", "Heals in 6 to 8 weeks"))

      Dim injury As OrthoInjury
      For Each injury In BrokenBones
        Console.WriteLine("Description: " & injury.Description)
      Next
      Console.ReadLine()
    End Sub

End Module

Public Class TypedCollection(Of T)
  Inherits System.Collections.CollectionBase

  Default Public Property Item(ByVal Index As Integer) As T
    Get
      Return CType(List(Index), T)
    End Get
    Set(ByVal value As T)
      List(Index) = value
    End Set
  End Property

  Public Function Add(ByVal value As T) As Integer
    Return List.Add(value)
  End Function

End Class

Public Class OrthoInjury
  Private FHasXray As Boolean
  Private FDescription As String
  Private FPrognosis As String

  Public Sub New(ByVal HasXray As Boolean, _
    ByVal Description As String, ByVal Prognosis As String)

    FHasXray = HasXray
    FDescription = Description
    FPrognosis = Prognosis
  End Sub

  Public Property HasXray() As Boolean
    Get
      Return FHasXray
    End Get
    Set(ByVal value As Boolean)
      FHasXray = value
    End Set
  End Property

  Public Property Description() As String
    Get
      Return FDescription
    End Get
    Set(ByVal value As String)
      FDescription = value
    End Set
  End Property

  Public Property Prognosis() As String
    Get
      Return FPrognosis
    End Get
    Set(ByVal value As String)
      FPrognosis = value
    End Set
  End Property
End Class

If you have read previous articles on typed collections, you will see that the generic typed collection (in bold in Listing 3) is basically a strongly typed collection with the data type parameterized.

Using Predefined Generic Classes

Fortunately, you are not required to define generic classes from scratch. The System.Collections.Generic namespace defines many of the classic data structures, such as List, Queue, and Stack, as generics already. You simply have to import that namespace and declare an instance of the type you want. For example, the following code adequately replaces your custom typed collection with the .NET 2.0 generic List typed collection:

Dim BrokenBones As System.Collections.Generic.List(Of OrthoInjury) = _
  New System.Collections.Generic.List(Of OrthoInjury)

As a general rule, if you want to store more than one type (heterogeneous types), use the older style classes like Queue and Stack. If you want to use just one type (homogeneous types), use the newer generic classes in the System.Collections.Generic namespace. Generally, you'll want the newer generic classes.

The Choice to Learn

Fewer and fewer things are separating traditionally complex languages like C++ from traditionally simpler languages like VB. At first glance, this fact can be frustrating because it implies VB is now harder to learn—like C++ is. In reality, the core language (VB) is pretty much the same, and just like C++, you always have the choice of learning more advanced idioms like generics if and when you need them. Now, you aren't handicapped by absence of choice.

Also, keep in mind that you always have the option of learning any programming idiom as a consumer first—use what's there like generic Lists—and then as a producer, learning how to create your own. Trying to master everything at once can be overwhelming and just isn't necessary.

About the Author

Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his upcoming book UML DeMystified from McGraw-Hill/Osborne (Spring 2005). Paul is also the founder and chief architect for Software Conceptions, Inc, founded 1990. He is available to help design and build software worldwide. You may contact him for consulting opportunities or technology questions at pkimmel@softconcepts.com.

If you are interested in joining, sponsoring a meeting, or posting a job, check out www.glugnet.org, the Web page of the Greater Lansing area Users Group for .NET.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: November 13, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT APIs can be a great source of competitive advantage. The practice of exposing backend services as APIs has become pervasive, however their use varies widely across companies and industries. Some companies leverage APIs to create internal, operational and development efficiencies, while others use them to drive ancillary revenue channels. Many companies successfully support both public and private programs from the same API by varying levels …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds