We saw how C# 1's collection namespace lacked the strong typing needed for better compile time type checking and in a lot of scenarios created performance issues by the excess use of explicit and implicit(foreach) casts.
We are going to continue on the same front. Generics in C# can be thought to have two types:
- Generic Types: These are basically wrapper classes around a group of elements(classes, delegates, interfaces and structures). Depending on the generic wrapper used, these elements may be held in an actual array(Lists<T>), doubly linked list(LinkedList<T>), hashtable(Dictionary<TKey, TValue) or a red-black tree(SortedDictonary<TKey, TValue>).
- Generic Methods: These are methods that introduce generic types themselves. These are more complicated to understand. We'll get back to them after we cover generic types.
As you saw above generics are usually shown by a name appended by a "<>" sign and with some words delimited by commas in between. The words within the angled brackets are called type parameters which are placeholders for any type that can be accepted for that generic. When we work with a certain generic type we have to declare the real type that the generic type would be working with. The real types are called type arguments. This is the same kind of idea of Parameters/Arguments in functions. As an example if we want to implement an actual dictionary we would have to declare our Dictionary<TKey, TValue> type to have type arguments of string. So the type parameters are going to be replaced by the type arguments in the declaration:
Dictionary<string, string> dic;
The generic types having its type parameters is called a unbound generic type since the type of it's arguments are still not known(Dictionary<TKey, TValue>). When a type's type arguments are specified it turns into a constructed type. Although the terminology is kind of confusing at first, we'll see why they are needed to understand some concepts as we move towards advanced generics. The dic variable for example in the above snippet is an instance of the constructed type (Dictionary<string, string>) which is a dictionary of strings to strings.
Generic methods are methods that introduce new types. Notice that methods that accept a constructed generic type or return a constructed generic type are not generic methods for example the function below is not generic:
public List<string> GetStrings(string[] stringArray);
The best way to understand generic methods is seeing examples of them. The ConvertAll<> method of the Array and List<T> types is a good example. This method has the following signature:
public List<TOutput> ConvertAll<TOutput> (Converter<TInput, TOutput> converter);
This declaration states that the return value of the function ConvertAll is a List of a certain type. Notice that the type is unspecified since TOutput is a type parameter not a real type or an type argument. The method also takes a generic delegate as input. This delegate receives a TInput type and returns a TOutput type and is called converter. The TOutput type returned by the delegate is the same type parameter declared in the function declaration.
Whenever the declarations of generic types or generic methods get too complicated to be understood abstractly. It is easier to replace type parameters with some arguments. Try this technique with the above generic method and things would start to make sense.
To see this method in action the snippet below uses this method to convert elements of a list from strings to integers:
public static int convert(string s)
{
int num = 0;
for(int i = 0; i < s.Length; i++)
num += s[i];
return num;
}
...
List<string> strings = new List<string>();
strings.Add("Hello");
strings.Add("This is a test");
strings.Add("Hey hey hey");
List<int> integers = strings.ConvertAll<int>(convert);
for (int i = 0; i < integers.Count; i++)
Console.WriteLine(integers[i]);
Console.ReadKey();
Unless you have experience with generic methods before this should still seem confusing. But don't worry it is just going to get even more confusing ! But I'm just joking of course...or am I? Anyways, the best to do now is to look at some generic types available throughout the library. I would continue this series of posts on generics with type constraints which let you define the type that the generic can wrap, then we would move to creating our own generic types and then finally move to advanced generics and finally at the end we'll have a comparison between generics in .Net with their counterparts in C++ and Java.
Until then, happy coding.
Until then, happy coding.
No comments:
Post a Comment