Using Generics in VB 2005

Using Generics in Visual Basic 2005

Generics is a new Visual Basic 2005 feature that many developers will likely miss on the first pass, but find they can't do without once they know about it. The term doesn't really tell you what this feature can do. Imagine writing a class that performs math tasks. Using standard code-writing techniques, you'd have to write separate methods for integers (in all their forms) and reals (in all their forms). Using generics, you can write the methods one time and let the .NET Framework handle the type issues for you. Generics can make your programming experience a lot more enjoyable. You receive the benefits of code reuse without all of the work.

What's the Big Deal?

You might wonder what generics have to offer, besides reduced typing; after all, you still have to create the class to process information. The best feature is that a properly constructed generic class can actually reduce security problems in your code. Because you define the class as using several datatypes, you don't have to rely on the generic Object type. A generic class relies on the same strong datatyping that other classes do when they work with a specific datatype. In short, although you've gained a lot of flexibility, the resulting code is actually better.

Using generic classes can also improve performance. One of the biggest improvements is that the .NET Framework won't use boxing for value types. Although a generic class can work with more than one datatype, it handles each datatype individually behind the scenes. This technique ensures that your application provides the best performance with the least amount of work on your part.

It reduces errors and debugging time as well

One of the amazing features of using a generic class is that IntelliSense can actually track the strong datatyping for you. Unlike using an object to create a generic method of handling multiple datatypes, your code relies on strong datatyping, which means that IntelliSense provides more information to you. This feature not only makes the code easier to work with; it reduces errors and debugging time as well.

Creating a Generic Class

When working with generics, choose classes that have a broad range of uses so that the time required to create the generic class will eventually pay you back in reduced development time. For example, collections are a type of class that you use with more than one datatype, and it's very likely that you'll use various forms of the same collection in multiple applications. Rather than build the collection from scratch each time, you can use generics to create a generic class prototype. To begin, you need to add the following Imports statement to your code:

Imports System.Collections.Generic

Once you add the Imports statement, you can create a generic class. The basic class will look familiar. You can use properties, functions, subs, fields, or anything else you'd normally use in a class, as shown in listing 1.

Public Class MyGenericCollection(Of ItemType)

  ' Define a generic collection.
  Private Items As Collection(Of ItemType)

  ' Create a constructor.
  Public Sub New()
    Items = New Collection(Of ItemType)
  End Sub

  ' Returns the number of items in the collection.
  Public ReadOnly Property Count() As Integer
    Get
      Return Items.Count
    End Get
  End Property

  ' Get or set a specific item.
  Default Public Property Item(ByVal Index As Integer)_
    As ItemType
    Get
      Return Items(Index)
    End Get
    Set(ByVal value As ItemType)
      Items(Index) = value
    End Set
  End Property

  ' Add a new item to the collection.
  Public Sub Add(ByVal Value As ItemType)
    Items.Add(Value)
  End Sub

  ' Remove an item from the collection.
  Public Sub RemoveAt(ByVal Item As Int32)
    Items.RemoveAt(Item)
  End Sub
End Class

Listing 1: Defining a Generic Class Type

The class declaration should look familiar, but notice the (Of ItemType) entry. This entry is what differentiates a generic class from a standard class. Think of it as a placeholder for the type that you supply later when instantiating the class. When the .NET Framework sees this entry, it replaces it with the type supplied by the code using the class. Of course, you don't have to use ItemType in your class—you can call this generic class anything, just as you would a variable.

Think of it as a placeholder for the type that you supply later when instantiating the class

Because this is a collection class, the first task the code performs is to create a global variable to hold the collection. However, because you want to associate this collection with the generic datatype, you use the special (Of ItemType) again to tell the .NET Framework to create a specific collection. This is where the strong datatyping comes into play. Even though you don't know the datatype the collection will use, the .NET Framework will, and can create a collection of that type for you.

As with all classes, make sure that you include a constructor or New() sub. The constructor instantiates the collection in this case. Again, be sure to use the (Of ItemType) code to ensure that the code treats the generic data correctly.

You can use the collection any way that you would normally. For example, the Count property returns the number of items in the collection. In fact, this code doesn't look any different from the code you'd use in a standard class.

At times, you do need to work with the specific item type. The Item property demonstrates this principle. Notice that the property returns a value of ItemType—you don't know the datatype now, but the .NET Framework assigns it later. In fact, when I created this class, the IDE automatically created the correct Set() method for me. Notice that it also relies on ItemType. However, using generics doesn't prevent you from creating standard property entries. For example, the Item property is the default for this class, as specified by the Default keyword.

Consequently, you don't need to worry about someone providing the wrong type of data, although the class itself accepts generic input

The class ends with two Subs. The first adds a new value to the collection based on ItemType; the second removes a value from the collection using the item number. A developer can't add the wrong datatype to the collection, because the .NET Framework assigns and monitors the datatype. Consequently, you don't need to worry about someone providing the wrong type of data, although the class itself accepts generic input. Contrast this functionality with using an Object, where the developer using your class could provide any kind of input and you'd need to perform tests to ensure that it's the correct type.

Using the Generic Class

Using a generic class in code is only a little different from using any other class. You still have to instantiate the class and you work with the methods, properties, and events as you would any other class. Listing 2 shows a typical example of how to use the generic class defined in listing 1.

Private Sub btnTest_Click( _
  ByVal sender As System.Object, _
  ByVal e As System.EventArgs) _
    Handles btnTest.Click

  ' Create a new collection.
  Dim CollectInt As New MyGenericCollection(Of String)

  ' Perform some tasks with it.
  CollectInt.Add("One")
  CollectInt.Add("Two")
  CollectInt.Add("Three")
  CollectInt.RemoveAt(1)

  ' Display some statistics.
  MessageBox.Show("Number of Entries: " + _
          CollectInt.Count.ToString())
  MessageBox.Show("Value of First Item: " + _
          CollectInt(0))
End Sub

Listing 2: Using a Generic Class

The btnTest_Click() method begins by creating a new collection. Notice how the code instantiates the object CollectInt. Instead of the normal method, it requires that the developer provide the datatype of the collection. In this case, the (Of String) entry specifies that this collection will accept only String values.

Once CollectInt is instantiated, the code uses its methods to add and remove values from the collection. There isn't anything different here from the way in which you interact with a standard class, so you really don't need to change any of the techniques you normally use to work with objects based on generic classes.

The code ends by displaying the number of items in the collection and the value of the first item, Two, after using the various methods to manipulate it. Again, you don't have to do anything special to work with the object. The Count property works much as you expect it would. When working with the default property, Item, you'll find that you don't have to perform a conversion to a String as you normally would. The reason is that the IDE and the .NET Framework both know that you created this collection to use a String datatype. In addition, IntelliSense also knows that this is a String datatype, as shown in Figure 1. Notice that you receive String-specific information, rather than a generic datatype, as you would when using Object.

Fig. 1: Using generics means getting datatype-specific feedback from IntelliSense.

Constraining a Generic Class

When you look at listing 1, you might begin to wonder how useful generic classes are, because the example class can accept any datatype. Fortunately, you can constrain a generic class to accept only specific datatypes. For example, you might create a datatype class called Address that looks like this:

Public Class Address
  Public Name As String
  Public Address1 As String
  Public Address2 As String
  Public City As String
  Public State As String
  Public ZIP As String
End Class

You can constrain the generic class to accept only an Address datatype as input by modifying the declaration. For example, you might want to change the collection in listing 1 like this:

Public Class MyGenericCollection(Of ItemType As Address)

This generic class will now accept just the Address datatype. Any other use of the class generates an error (the IDE refuses to compile the code). You can use any generic type, as well as interfaces, as a constraint. For example, you might want to define a generic class that only accepts datatypes that implement the ICustomFormatter interface.

Defining Multiple Types for a Generic Class

You can't define every class using just one datatype. Fortunately, generic classes also accommodate multiple datatypes. All you need to do is separate datatypes with a comma, as shown here:

Public Class MyGenericCollection(Of ItemType1, ItemType2, ItemType3)

This declaration accepts three datatypes (no more, no less) as inputs. In this case, the code doesn't place any constraints on the datatypes, but you can easily add constraints as needed. In fact, you can mix-and-match datatypes with and without constraints in the same declaration and the datatypes that do have constraints need not have the same constraint type. You could include one datatype that uses a specific class and another that relies on an interface.

Generically Speaking

Although generic classes might seem like only an incremental step at first, they're actually a very big leap in the evolution of .NET programming. Using generics frees you from having to create a new class for every datatype, improves performance, and reduces both development and debugging time. To get the most out of Visual Studio 2005, you really need to try this new feature—you'll find that you like it a lot.

This article is reprinted with permission of InformIT.com.

Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar