Wrox Home  
Professional .NET 2.0 Generics
by Tod Golding
October 2005, Paperback

Type Parameters

By now, you should be comfortable with the idea of type parameters and how they serve as a type placeholder for the type arguments that will be supplied when your generic class is constructed. Now let's look at what, precisely, can appear in a type parameter list for a generic class.

First, let's start with the names that can be assigned to type parameters. The rules for naming a type parameter are similar to the rules used when defining any identifier. That said, there are guidelines that you should follow in the naming of your type parameters to improve the readability and maintainability of your generic class. These guidelines, and others, are discussed in Chapter 10, "Generics Guidelines" of Professional .NET 2.0 Generics.

A generic class may also accept multiple type parameters. These parameters are provided as a delimited list of identifiers:

[VB code]
Public Class Stack(Of T, U, V)
[C# code]
public class Stack<T, U, V> 

As you might suspect, each type parameter name must be unique within the parameter list as well as within the scope of the class. You cannot, for example, have a type parameter T along with a field that is also named T. You are also prevented from having a type parameter and the class that accepts that parameter share the same name. Fortunately, the names you're likely to use for type parameters and classes will rarely cause collisions.

In terms of scope, a type parameter can only be referenced within the scope of the generic class that declared it. So, if you have a child generic class B that descends from generic class A, class B will not be able to reference any type parameters that were declared as part of class A.

The list of type parameters may also contain constraints that are used to further qualify what type arguments can be supplied of a given type parameter. Chapter 7, "Generic Constraints" (Professional .NET 2.0 Generics) looks into the relevance and application of constraints in more detail.

Overloaded Types

The .NET implementation of generics allows programmers to create overloaded types. This means that types, like methods, can be overloaded based on their type parameter signature. Consider the declarations of the following types:

[VB code]
Public Class MyType
End Class

Public Class MyType(Of T)
End Class

Public Class MyType(Of T, U)
End Class
[C# Code]
public class MyType {

public class MyType<T> {

public class MyType<T, U> {

Three types are declared here and they all have the same name and different type parameter lists. At first glance, this may seem invalid. However, if you look at it from an overloading perspective, you can see how the compiler would treat each of these three types as being unique. This can introduce some level of confusion for clients, and this is certainly something you'll want to factor in as you consider building your own generic types. That said, this is still a very powerful concept that, when leveraged correctly, can enrich the power of your generic types.

Static Constructors

All classes support the idea of a static (shared) constructor. As you might expect, a static constructor is a constructor that can be called without requiring clients to create an instance of a given class. These constructors provide a convenient mechanism for initializing classes that leverage static types.

Now, when it comes to generics, you have to also consider the accessibility of your class's type parameters within the scope of your static constructor. As it turns out, static constructors are granted full access to any type parameters that are associated with your generic classes. Here's an example of a static constructor in action:

[VB code]
Imports System.Collections.Generic

Class MySampleClass(Of T)
    Private Shared _values As List(Of T)

    Shared Sub New()
        If (GetType(T).IsAbstract = False) Then
            Throw New Exception("T must not be abstract")
            _values = New List(Of T)()
        End If
    End Sub
End Class
[C# code]
using System.Collections.Generic;

public class <b>MySampleClass</b><T> {
    private static <b>List</b><T> _values;

    static <b>MySampleClass</b>() {
        if (typeof(T).IsAbstract == false)
            throw new <b>Exception</b>("T must not be abstract");
            _values = new <b>List</b><T>();

This example creates a class that accepts a single type parameter, T. The class has a data member that is used to hold a static collection of items of type T. However, you want to be sure, as part of initialization, that T is never abstract. In order to enforce this constraint, this example includes a static constructor that examines the type information about T and throws an exception if the type of T is abstract. If it's not abstract, the constructor proceeds with the initialization of its static collection.

This is just one application of static constructors and generic types. You should be able to see, from this example, how static constructors can be used as a common mechanism for initializing any generic class that has static data members.