Generics - Overview


A generic is an item that is parameterized by a type. Classes, interfaces, methods and delegates may be parameterized in this way. By using generics, a class can be made to operate on data of differing types. For example, a linked list class can be made to be a linked list of any type. A class, interface, method or delegate that is parameterized is called a generic, as in generic class or generic method.

A very simple example of a generic class is shown below.

// Generic1 - A Simple Class

using System;

class Generic<T>
{
    T t;

    public Generic(T tIn) { t = tIn; }

    public T GetObject() { return t; }

    public string GetTypeName() {return typeof(T).ToString(); }
}

class Program
{
    static void Main()
    {
        Generic<int> gi = new Generic<int>(99);
        int i = gi.GetObject();
        Console.WriteLine("Object value is: {0}", i);
        Console.WriteLine("Generic parameter type name is: {0}", gi.GetTypeName());

        Generic<string> gs = new Generic<string>("Generics are good");
        string s = gs.GetObject();
        Console.WriteLine("Object value is: {0}", s);
        Console.WriteLine("Generic parameter type name is: {0}", gs.GetTypeName());
    }
}

The generic class is called Generic and it is parameterized by the class T. To indicate that T is a type parameter, it is enclosed within angle brackets - as in <T>. The declaration

class Generic<T>
{
 ...
}

represents a family of classes where each class is obtained by substituting a particular type for T. T is used by convention only, and any identifier could be used. Likewise, "Generic" is the chosen generic class name but any suitable class name may be chosen.

The generic class Generic<T> is then defined in terms of T, where the intention is that T will be ultimately substituted. The next step in the declaration is shown below.

class Generic<T>
{
    T t;
    ...
}

The only field of Generic<T> is a T called t. If T is an int, then t is an int. If T is a string, then t is a string. The rest of the class then operates on the data, defining general operations that apply to any class that may be substituted. For example, the next step is to supply a constructor, as shown below.

class Generic<T>
{
    T t;

    public Generic(T tIn) { t = tIn; }
    ...
}

The constructor accepts a T as a parameter and it copies the specified value to the field t. This works equally for an int, a double or a string (or any other type). In this way, we can see that the class is being built based upon the parameterized type. Two more operations are defined and they are shown below.

class Generic<T>
{
    T t;

    public Generic(T tIn) { t = tIn; }

    public T GetObject() { return t; }

    public string GetTypeName() {return typeof(T).ToString(); }
}

The method GetObject() returns the t. This shows that the return type of a method is parameterized by the type. Input parameters can be parameterized in the same way. The method GetTypeName() determines the name of the type T in the form of a string and returns that string. This completes the declaration of the generic class, now it remains to be seen how to use this class. The first statement inside Main() is shown again.

Generic<int> gi = new Generic<int>(99);

The new operator creates a specific version of Generic<T> - a Generic<int>. This binds the type int as the generic class parameter. It specialized the type. Generic<T> may be referred to as an open constructed type whereas Generic<int> is referred to as a closed constructed type. Generic<T> is a family of classes where as Generic<int> is a particular class. The reader should visualize the full class name of this class as Generic<int>. Generic<string> is a different class derived from the same generic.

When Generic<int> is constructed it is passed the integer argument 99 and the returned reference is assigned to a reference variable called gi. This completes the construction of the class. Several methods are then called, resulting in the following output.

Object value is: 99
Generic parameter type name is: System.Int32
Object value is: Generics are good
Generic parameter type name is: System.String

The type of int has been printed as System.Int32, which is its actual name.

Further down in main, the generic is instantiated again, this time with the following statement.

Generic<string> gs = new Generic<string>("Generics are good");

This time the generic class parameter is a string. Looking back at the generic class, when T is a string, the field t is a string (whereas previously it was an int). It can be seen that this second instantiation produces an entirely different class.

The General Form of a Generic Class

The previous example had a single generic parameter. Multiple generic parameters may be specified in a comma separated list. The general form of a generic class is as follows.

class class-name<type-parameter-list>
{
 ...
}

The syntax for declaring a reference to a generic class is as follows.

class-name<type-argument-list> variable-name =
   new class-name<type-argument-list>(constructor-argument-list);

Note that generic structures (struct) may be created with the same syntax.