Generics in .Net – Part 1 – The Basics

One of the most powerful features in my opinion about .Net, which really sets it apart from other language frameworks, is its implementation of Generics.  This post will be the first in a multiple part series on the subject.

What is Generics?

So, what is Generics and how do we use it?  Generics or generic programming is a form of coding where type parameters are declared in the signature of classes or methods in place of specific types so that they can be specified later as type arguments.  These type parameters are then used in place of specific types in the implementation of those classes or methods where they are defined.  The following are examples of declared type parameters on a class and on a couple of methods of a normal class:

public  class   GenericClass<T>
{
    private T   someValue;

    public      GenericClass(T someValue) { this.someValue = someValue; }
}

public  class   NormalClass
{

    public  T       GetValue<T>(string key) {...}

    public  void    SetValue<T>(string key, T value) {...}

}

In the above examples, <T> declares that the class or method where it is defined contains zero, one or more references to an as of yet undeclared type T that will be specified later.  Once specified, all instances where T is used will effectively be substituted with the type specified.

Consuming Generic Classes

Perhaps the most common use of Generics in .Net is the use of the Generic classes found within the System.Collections.Generic namespace.  And of these classes, perhaps the most commonly used is the List<T> class.  The List<T> class is the Generic equivalent of the ArrayList class.  It is virtually identical in function as both implement the IList interface and both essentially contains and manage an array of items.  Where the List<T> class shines is in the type safety of ensuring that each instance of the class contains only a homogeneous list of type T or its derivatives.  ArrayList by comparison is homogenous only to the System.Object type.  Since nearly all types derive from System.Object, the ArrayList class does not do much to ensure type safety in most practical cases, especially when one has a specific sub type in mind that they wish to have a homogeneous list of.

So the List<T> class allows you to specify a type argument for parameter T that “constrains” T to a specific type or its derivatives.  When you construct an instance of List<T> at run-time, the type argument that you provide is effectively substituted for T all throughout the implementation of List<T> and a new class type is created.  Any further instances created using the same type argument for T are also instances of this new class type.

One of the main advantages of this form of reuse is in leveraging the same general functionality of a List type across multiple item types without have to repeat write the same general code.  Take the following for example class snippets:

public  class   IntList
{
    ...
    public  int GetValueAtIndexOrDefault(int index, int defaultValue)
    {
        int returnValue;
        if (!this.TryGetValue(index, out returnValue))  returnValue = defaultValue;
        return returnValue;
    }
    ...
}

public  class   StringList
{
    ...
    public  string  GetValueAtIndexOrDefault(int index, string defaultValue)
    {
        string  returnValue;
        if (!this.TryGetValue(index, out returnValue))  returnValue = defaultValue;
        return returnValue;
    }
    ...
}

Consider how the two implementations of GetValueAtIndexOrDefault are virtually identical.  They vary only between the use of the types int and string with regards to the type of items each type of list contains.  Using generics, the above two implementation could be abstracted into the following:

public  class   List<T>
{
    ...
    public  T   GetValueAtIndexOrDefault(int index, T defaultValue)
    {
        T   returnValue;
        if (!this.TryGetValue(index, out returnValue))  returnValue = defaultValue;
        return returnValue;
    }
    ...
}

Notice how the code is now DRYed up.  The type parameter T is now substituted for whatever type arguments we want to specify.  To effectively replace the IntList and StringList class types, we would create instances of List<int> and List<string> respectively.

I plan on posting more on the topic of generics in future posts covering the following concepts:

Generics Inheritance (Parametric Polymorphism)

Understanding Generic Type Parameter Constraints (Bounded Parametric Polymorphism)

Subclass Type Parameter Constraint

Generic Namespaces