I think a good C# developer needs to get a handle on .NET generics. Most of the advanced features in C# deal with lots of generics and having a very good understanding of generics will help considerably, most especially when dealing with generic delegates. So here in this post, we will review generics.
Generic type definitions can be methods, classes, structures, and interfaces.
The placeholders (e.g. <T>) are called generic type parameters, or type parameters.
You specify the actual types to substitute for the type parameters during instantiation.
When instantiated, a generic type definition becomes a constructed generic type.
You can place limits or constraints on generic type parameters.
A method is considered a generic method definition if it has two parameter lists: a list of type parameters enclosed in <> and a list of formal parameters enclosed in (). A method belonging to a generic or non-generic type does not make the method generic or non-generic. Only the existence of the two parameter lists will make the method generic, as in the example below.
A type nested in a generic type is considered by CLR to be generic even if it doesn’t have generic type parameters of its own. When you instantiate a nested type, you need to specify the type arguments for all enclosing generic types.
The following are some common generic collection counterparts provided by the .NET framework:
- Dictionary<TKey, TValue> which is the generic version of Hashtable. It uses KeyValuePair<TKey, TValue> for enumeration instead of DictionaryEntry.
- List<T> which is the generic version of ArrayList.
- Queue<T> and Stack<T> which is the generic versions of collections with same names.
- SortedList<TKey, TValue> which is a hybrid of a dictionary and list, just like it’s nongeneric version of same name.
- SortedDictionary<TKey, TValue> which is a pure dictionary, and LinkedList<T>. Both don’t have nongeneric versions.
- Collection<T> which is a base class for generating custom collection types, ReadOnlyCollection<T> which provides read-only collection from any type implementing IList<T>, and KeyedCollection<TKey, TItem> for storing objects containing their own keys.
There are also generic interface counterparts for ordering and equality comparisons and for shared collection functionality:
- System.IComparable<T> and System.IEquatable<T> which define methods for ordering comparisons and equality comparisons.
- IComparer<T> and IEqualityComparer<T> in the System.Collections.Generic namespace, which offer alternative way for types that do not implement System.IComparable<T> and System.IEquatable<T>. They are used by methods and constructors of many of the generic collection classes. An example would be passing a generic IComparer<T> object to the constructor of SortedDictionary<TKey, TValue> to specify a sort order. Generic classes Comparer<T> and EqualityComparer<T> are their base class implementations.
- ICollection<T> which provides basic functionality for adding, removing, copying, and enumerating elements in a generic collection type. It inherits from IEnumerable<T> and the nongeneric IEnumerable.
- IList<T> which extends ICollection<T> with methods for indexed retrieval.
- IDictionary<TKey, TValue> which extends ICollection<T> with methods for keyed retrieval. Generic dictionary types also inherit from nongeneric IDictionary.
- IEnumerable<T> which provides a generic enumerator structure used by foreach. It inherits from nongeneric IEnumerator because MoveNext and Reset methods appear only on the nongeneric interface. This means consumer of the nongeneric interface can also consume the generic interface because the generic interface provides for nongeneric implementation.
You also have generic delegates in .NET framework. An example is the EventHandler<TEventArgs> which you can use in handling events with custom event arguments. No need to declare your own delegate type for the event. If you need to brush up on events and delegates, see my post on raising events and nongeneric delegates.
There are also a bunch of useful generic delegates available for manipulating arrays and lists
- Action<T> which allows you to perform action on an element by passing an Action<T> delegate instance and an array to the generic method Array.ForEach<T>. You can also pass an Action<T> delegate instance to the nongeneric method List<T>.ForEach.
- Predicate<T> which allows you to specify a search criteria to Array’s Exists<T>, Find<T>, FindAll<T>, and so on and also to List<T>’s Exists, Find, FindAll, and so on.
- Comparsion<T> which allows you to provide a sort order.
- Converter<TInput, TOutput> which allows you to convert between two types of arrays or lists.
Ok so that’s all we have for generics. Just a review of the basics that a C# developer need to know.