I'm going to keep this as simple as possible. Your main goal is to gain an appreciation of why we would want to use generics in the first place. The typical candidate scenario for the use of generics is where we have a class that has a member variable, and the type of this variable should be defined by the client of the class, and not by the programmer. Likewise, for a method that has a parameter passed to it. In other words, the code in our classes and methods remains the same and the types of the data can change with each use. Think code reuse.
Up to now, this type of generality involved writing the same code over and over for each type you wanted to accomodate. And with collections, you would typically have to use the object class and then cast back. This would involve boxing and unboxing for value types, which would incur heavy performance hits, depending on the size of your collections. More importantly, when casting back, you could not be certain of the type in the collection. This could result in runtime errors after your code had shipped. More than anything else, the type safety afforded by generics is its biggest selling point. Type-safe code is the easiest code to maintain, plain and simple.
With generics, we declare type-parameterized code which we can instantiate with different types. We write the code with '<T>' placeholders for types and then plug in the actual types when we are creating an instance. We don't have to use the letter 'T', it's just convention.
// 1.1 Loosely-Typed Collection
Class Stack
{
public object Pop();
...
}
Stack s = new Stack();
s.Push(1);
s.Push(2);
// Cast necessary
int i = (int) s.Pop();
// 2.0 Strongly-Typed Collection
Class Stack<T>
{
public <T> Pop();
...
}
Stack<int> s = new Stack<T>();
s.Push(1);
s.Push(2);
// No cast necessary
int i = s.Pop();
You'll probably find the most ready use for generics when implementing collections. The generic collection classes are part of the C# 2.0 System.Collections.Generic namespace. Below is a list of the traditional 1.1 collections and their new 2.0 equivalents (from Krzysztof Cwalina):
Non-Generic |
Similar Generic Type |
ArrayList |
List<T> |
Hashtable |
Dictionary<TKey,TValue> |
SortedList |
SortedList<TKey,TValue> |
Queue |
Queue<T> |
Stack |
Stack<T> |
IEnumerable |
IEnumerable<T> |
ICollection |
N/A (use IEnumerable<T> or anything that extends it) |
N/A |
ICollection<T> |
IList |
IList<T> |
CollectionBase |
Collection<T> |
ReadOnlyCollectionBase |
ReadOnlyCollectionBase<T> |
DictionaryBase |
N/A (just implement IDictionary<TKey,TValue>) |
N/A |
SortedDictionary<TKey,TValue> |
N/A |
KeyedCollection<TKey,TItem> |
N/A |
LinkedList<T> |
In the next part, I'll try and cut through the jargon attached to generics and also discuss some practical uses for generics in everyday coding tasks. In the meantime, check out this excellent Overview of Generics in the .NET Framework.