Section 2.5. Build Typesafe Generic Classes


2.5. Build Typesafe Generic Classes

Programmers often face a difficult choice. On one hand, it's keenly important to build solutions that are as generic as possible, so that they can be reused in different scenarios. For example, why build a CustomerCollection class that accepts only objects of type Customer when you can build a generic Collection class that can be configured to accept objects of any type? On the other hand, performance and type safety considerations can make a generic solution less desirable. If you use a generic .NET Collection class to store Customer objects, for example, how can you be sure that someone won't accidentally insert another type of object into the collection, causing an insidious problem later on?


Note: Need to create a class that's flexible enough to work with any type of object, but able to restrict the objects it accepts in any given instance? With generics, VB has the perfect solution.

Visual Basic 2005 and .NET 2.0 provide a solution called generics. Generics are classes that are parameterized by type. In other words, generics allow you to create a class template that supports any type. When you instantiate that class, you specify the type you want to use, and from that point on, your object is "locked in" to the type you chose.

2.5.1. How do I do that?

An example of where the use of generics makes great sense is the System.Collections.ArrayList class. ArrayList is an all-purpose, dynamically self-sizing collection. It can hold ordinary .NET objects or your own custom objects. In order to support this, ArrayList treats everything as the base Object type.

The problem is that there's no way to impose any restrictions on how ArrayList works. For example, if you want to use ArrayList to store a collection of Customer objects, you have no way to be sure that a faulty piece of code won't accidentally insert strings, integers, or some other type of object, causing future headaches. For this reason, developers often create their own strongly typed collection classesin fact, the .NET class library is filled with dozens of them.

Generics can solve this problem. For example, using generics you can declare a class that works with any type using the Of keyword:

Public Class GenericList(Of ItemType)     ' (Code goes here) End Class

In this case, you are creating a new class named GenericList that can work with any type of object. However, the client needs to specify what type should be used. In your class code, you refer to that type as ItemType. Of course, ItemType isn't really a typeit's just a placeholder for the type that you'll choose when you instantiate a GenericList object.

Example 2-2 shows the complete code for a simple typesafe ArrayList.

Example 2-2. A typesafe collection using generics
Public Class GenericList(Of ItemType)     Inherits CollectionBase          Public Function Add(ByVal value As ItemType) As Integer         Return List.Add(value)     End Function          Public Sub Remove(ByVal value As ItemType)         List.Remove(value)     End Sub          Public ReadOnly Property Item(ByVal index As Integer) As ItemType         Get             ' The appropriate item is retrieved from the List object and              ' explicitly cast to the appropriate type, and then returned.             Return CType(List.Item(index), ItemType)         End Get     End Property End Class

The GenericList class wraps an ordinary ArrayList, which is provided through the List property of the CollectionBase class it inherits from. However, the GenericList class works differently than an ArrayList by providing strongly typed Add( ) and Remove( ) methods, which use the ItemType placeholder.

Here's an example of how you might use the GenericList class to create an ArrayList collection that only supports strings:

' Create the GenericList instance, and choose a type (in this case, string). Dim List As New GenericList(Of String)      ' Add two strings. List.Add("blue") List.Add("green")      ' The next statement will fail because it has the wrong type. ' There is no automatic way to convert a GUID to a string. ' In fact, this line won't ever run, because the compiler ' notices the problem and refuses to build the application. List.Add(Guid.NewGuid( ))

There's no limit to how many ways you can parameterize a class. In the GenericList example, there's only one type parameter. However, you could easily create a class that works with two or three types of objects, and allows you to make all of these types generic. To use this approach, just separate each parameter type with a comma (between the brackets at the beginning of a class).

For example, consider the following GenericHashTable class, which allows you to define the type of the items the collection will store (ItemType), as well as the type of the keys you will use to index those items (KeyType):

Public Class GenericHashTable(Of ItemType, KeyType)     Inherits DictionaryBase     ' (Code goes here.) End Class

Another important feature in generics is the ability to apply constraints to parameters. Constraints restrict the types allowed for a given generic class. For example, suppose you want to create a class that supports only types that implement a particular interface. To do so, first declare the type or types the class accepts and then use the As keyword to specify the base class that the type must derive from, or the interface that the type must implement.

Here's an example that restricts the items stored in a GenericList to serializable items. This feature would be useful if, for example, you wanted to add a method to the GenericList that required serialization, such as a method that writes all the items in the list to a stream:

Public Class SerializableList(Of ItemType As ISerializable)    Inherits CollectionBase      ' (Code goes here.) End Class

Similarly, here's a collection that can contain any type of object, provided it's derived from the System.Windows.Forms.Control class. The end result is a collection that's limited to controls, like the one exposed by the Forms.Controls property on a window:

Public Class ControlCollection(Of ItemType As Control)    Inherits CollectionBase      ' (Code goes here.) End Class

Sometimes, your generic class might need the ability to create the parameter class. For example, the GenericList example might need the ability to create an instance of the item you want to store in the collection. In this case, you need to use the New constraint. The New constraint allows only parameter types that have a public zero-argument constructor, and aren't marked MustInherit. This ensures that your code can create instances of the parameter type. Here's a collection that imposes the New constraint:

Public Class GenericList(Of ItemType As New)    Inherits CollectionBase      ' (Code goes here.) End Class

It's also worth noting that you can define as many constraints as you want, as long as you group the list of constraints in curly braces, as shown here:

Public Class GenericList(Of ItemType As {ISerializable, New})    Inherits CollectionBase      ' (Code goes here.) End Class

Constraints are enforced by the compiler, so if you violate a constraint rule when using a generic class, you won't be able to compile your application.


Note: Generics are built into the Common Language Runtime. That means they are supported in all first-class . NET languages, including C#.

2.5.2. What about...

...using generics with other code structures? Generics don't just work with classes. They can also be used in structures, interfaces, delegates, and even methods. For more information, look for the index entry "generics" in the MSDN Help. For more in-depth examples of advanced generic techniques, you can refer to a Microsoft whitepaper at http://www.msdn.net/library/en-us/dnvs05/html/vb2005_generics.asp.

Incidentally, the .NET Framework designers are well aware of the usefulness of generic collections, and they've already created several for you to use out of the box. You'll find them in the new Systems.Collections.Generic namespace. They include:

  • List (a basic collection like the GenericList example)

  • Dictionary (a name-value collection that indexes each item with a key)

  • LinkedList (a linked list, where each item points to the next item in the chain)

  • Queue (a first-in-first-out collection)

  • Stack (a last-in-first-out collection)

  • SortedList (a name-value collection that's kept in perpetually sorted order)

Most of these types duplicate one of the types in the System.Collections namespace. The old collections remain for backward compatibility.



Visual Basic 2005(c) A Developer's Notebook
Visual Basic 2005: A Developers Notebook
ISBN: 0596007264
EAN: 2147483647
Year: 2006
Pages: 123

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net