What are Generics?
The .NET framework version 1.1 supported the creation of various types of collection. These collections allow a number of objects to be gathered together in various structures, such as simple lists, hash tables, stacks and queues. One of the key drawbacks with all of the .NET 1.1 collections is that they are not type-safe. This means that any value or object can be added to a collection, with all reference types being cast as objects and value types being boxed.
The lack of type safety can cause logical problems. For example, if you have a collection that you are using solely to hold a group of "Customer" objects, it is possible to accidentally add an "Order" object to the list. If you later attempt to read the Order object back from the collection and convert it to a Customer, the conversion will fail and cause a run-time exception. Additionally, the casting or boxing of all items in the collection to hold them as objects adds a processing overhead, as does unboxing or casting back to the original type. This overhead can cause performance issues.
To overcome these problems in a .NET 1.1 project, it was common to manually code type-safe collections. In the case of the Customer object, a "CustomerCollection" class with all of the necessary methods, properties and events would be created. This would remove the processing overheads of casting and ensure that if an attempt were made to add the incorrect type of object, it would be caught with a compiler error. Unfortunately the capacity for reuse of the CustomerCollection code would be limited. If a similar collection were required to hold Orders, it would need to be created separately, even though the code would be similar.
With the .NET version 2.0, Microsoft introduced Generics. Generic programming allows type-safe classes and methods to be created without specifying the types that they operate on. The types are declared, using type parameters, only when the class is used, allowing different instances to work with different types. This overcomes both of the previously stated problems. As the classes are type-safe, there is no requirement for boxing or casting when reading from or writing to a collection. This leads to performance improvements of 100% or more. As the types are set when a generic class is instantiated, the code can be reused, minimising duplication and increasing developer productivity.
Using Generic Types
Many generic types are included in the .NET framework. These include some generic collections that solve the problems described earlier without the requirement to write any extra code. You can find some of these in the System.Collections.Generic namespace. To follow the examples in the article, add the following using directive to your code:
To demonstrate the use of a generic type, we can use the List<T> class, spoken as "List of T". This is similar to the ArrayList class in that it provides a simple collection of objects that can be accessed using an index number. The important part to note is the "T" element. This is the type parameter that determines the type of objects or values that may be held in the collection. For example, the following code creates a List of integers; hence the collection is declared using "List<int>".
List<int> integers = new List<int>();
int extracted = integers;
Unlike with an ArrayList, the final line of this sample does not need to unbox the integer into the extracted variable. This improves the performance. To prove the type-safety of the collection, try adding the following line, which attempts to add a floating-point value to the list. Instead of allowing this and potentially causing a problem at run-time, the code will fail to compile.
The key benefit of a generic class is its ability to be reused for another type. Try running the following code, which is almost identical to the first example except for the use of strings instead of integers.
List<string> strings = new List<string>();
string extracted = strings;
Another useful generic collection is Dictionary<TKey, TValue>. This is similar to the Hashtable class, in that it stores a key and a value for each element in the collection and allows a value to be retrieved quickly using the key as a lookup. It also demonstrates that a generic type may include more than one type parameter. In this case, the key types and value types are declared using the TKey and TValue type parameters respectively. In the example below, integers are used for the keys and strings for the values.
Dictionary<int, string> items = new Dictionary<int,string>();
string extracted = items;
11 April 2010