Generics give you the ability to create generic methods or a generic type by defining a placeholder for method arguments or type definitions, that are specified at the time of invoking the generic method or creating the generic type.
To understand the importance of generics let's start by seeing some kind of problems that can be solved by them.
Let's start by creating an ArrayList to hold stack-allocated data.
As you may know, the ArrayList collection can receive and return only an Object type, so the runtime will convert the value type (stack-allocated) automatically via boxing operation into an Object (heap-allocated) as in the following:
To retrieve the value from the ArrayList you must unbox the heap-allocated object into a stack-allocated integer using a casting operation.
The problem with the stack/heap memory transfer is that it can have a big impact on performance of your application because when you use boxing and unboxing operations the following steps occur:
- A new object must be allocated on the managed heap.
- The value of the stack-allocated type must be transferred into that memory location.
- When unboxed, the value stored on the heap must be transferred back to the stack.
- The unused object on the heap will be garbage collected.
Now consider that your ArrayList contained thousands of integers that are manipulated by your program, this for sure will have an affect on your application performance.
Custom Collections
Assume that you need to create a custom collection that can only contain objects of the Employee type.
Now we will build the custom collection as in the following:
The problem here is that if you have many types in you application then you need to create multiple custom collections, one for each type. And as you can see, we also have the problem of boxing and unboxing here.
All problems you saw previously can be solved using generics, so let's see what we can do.
The System.Collections.generic namespace
You can find many generic collections in the System.Collections.Generic just like:
-
List<T>
-
Dictionary<K, V>
-
Queue<T>
-
Stack<T>
Generic collections allow you to delay the specification of the contained type until the time of creation.
By using the generic collection you avoid performance problems of the boxing and unboxing operations and don't need to create a custom collection for each type in you application.
With the generic collections it's up to you to define the type that will be contained in the collection by replacing the placeholder T by the type you want at the time of creation.
List<T>
The List<T> is a generic collection that represents a strongly typed list of objects that can be accessed by index. It is just like the non-generic collection ArrayList.
The following is an example of a List<T>:
Queue<T>
Queue<T> is a generic collection that represents a first-in, first-out (FIFO) collection of objects. It is just like the non-generic collection Queue.
The following is an example of a Queue<T>:
Stack<T>
Stack<T> is a generic collection that represents a last-in-first-out (LIFO) collection of instances of the same arbitrary type. It is just like the non-generic collection Stack.
The following is an example of a Stack<T>:
Dictionary<K, V>
Dictionary<K, V> is a generic collection that contains data in Key/Value pairs, it is just like the non-generic collection Hashtable.
The following is an example of a Dictionary<K, V>:
Generic Methods
You can create generic methods that can operate on any possible data type.
To define a generic method you specify the type parameter after the method name and before the parameters list.
The following is an example of a generic method:
You can define the type you want at the time you invoke the method.
The result will be a ???.
The parameter type is a System.Int32.
The result will be a ???.
The parameter type is a System.String.
You can also create a generic method without parameters as follows:
Here we see the method in use:
The result will be:
The type of x is a System.Int32.
The result will be:
The type of x is a System.String.
Note: you can omit the type parameter if the generic method requires arguments, because the compiler can infer the type parameter based on the member parameters. However if your generic method doesn't take any parameters then you are required to supply the type parameter or you will have a compile error.
Example:
In the case of a generic method that doesn't take parameters
In Part II you will see how to create generic classes, structures, delegates, interfaces and you will learn how to create a custom generic collection.