Reader Level:
ARTICLE

Generics in C# - Part I

Posted by Amr Monjid Articles | C# Language March 28, 2008
In Part I of this series you will see the importance of generics, you will learn how to use generic types which in the System.Collections.Generic namespace and you will also learn how to create generic methods.
  • 2
  • 0
  • 101781
Download Files:
 

Introduction:

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.
 
Why Generics?


To understand the importance of generics let's start by seeing some kind of problems that can be solved by them.
 
ArrayList:

Let's start by creating an ArrayList to hold stack-allocated data.
 
ArrayList intList = new ArrayList();
 
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:

ArrayList intList = new ArrayList();
//add a value type to the collection(boxing)
intList.Add(5);

To retrieve the value from the ArrayList you must unbox the heap-allocated object into a stack-allocated integer using a casting operation.

//unboxing
int x = (int)intList[0];
 
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:
  1. A new object must be allocated on the managed heap.
  2. The value of the stack-allocated type must be transferred into that memory location.
  3. When unboxed, the value stored on the heap must be transferred back to the stack.
  4. 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.

public class Employee

{

    string FirstName;

    string LastName;

    int Age;

   

    public Employee(){}

    public Employee(string fName, string lName, int Age)

    {

        this.Age = Age;

        this.FirstName = fName;

        this.Lastname = lName;

    }

    public override string ToString()

    {

        return String.Format("{0} {1} is {2} years old", FirstName, LastName, Age);

    }

}

Now we will build the custom collection as in the following:


public class EmployeesCollection : IEnumerable

{

    ArrayList alEmployees = new ArrayList();

    public EmployeesCollection() { }

 

     //Insert Employee type

     public void AddEmployee(Employee e)

     {

        //boxing

        alEmployees.Add(e);

    }

 

    //get the employee type

    public Employee GetEmployee(int index)

    {

        //unboxing

        return (Employee)alEmployees[index];

    }

 

    //to use foreach

    IEnumerator IEnumerable.GetEnumerator()

    {

        return alEmployees.GetEnumerator();

    }

}
 
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:

  1. List<T>
  2. Dictionary<K, V>
  3. Queue<T>
  4. 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>:

//Can only contain int type

List<int> intList = new List<int>();

 

//no boxing

intList.Add(10);

 

//no unboxing

int x = intList[0];

 

//Can only contain Employee objects

List<Employee> empList = new List<Employee>();

 

//no boxing

empList.Add(new Employee("Amr", "Ashush", 23));

 

//no unboxing

Employee e = empList[0]; 

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>:

//A generic Queue collection

Queue<int> intQueue = new Queue<int>();

 

//Add an int to the end of the queue

//(no boxing)

intQueue.Enqueue(5);

 

//Returns the int at the beginning of the queue

//without removing it.

//(no unboxing)

int x = intQueue.Peek();

 

//Removes and returns the int at the beginning of the queue

//(no unboxing)

int y = intQueue.Dequeue(); 

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>:

Stack<int> intStack = new Stack<int>();

 

//Insert an int at  the top of the stack

//(no boxing)

intStack.Push(5);

 

//Returns the int at the top of the stack

//without removing it.

//(no unboxing)

int x = intStack.Peek();

 

//Removes and returns the int at the top of the stack

//(no unboxing)

int y = intStack.Pop();


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>:

Dictionary<string, string> dictionary = new Dictionary<string, string>();

 

//Add the specified key and value to the dictionary

dictionary.Add("Key", "Value");

 

//Removes the value with the specified key from the dictionary

dictionary.Remove("Key");

 

//get the number of the Key/Value pairs contained in the dictionary

int count = dictionary.Count;
 
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:


public void MyGenericMethod<T>(T x, T y)

{

    Console.Writeline("Parameters type is {0}", typeof(T));

}

 

You can define the type you want at the time you invoke the method.

 

int x, y;

MyGenericMethod<int>(x, y);

 

The result will be a ???.

The parameter type is a System.Int32.

 

string x, y;

MyGenericMethod<string>(x, y);
 
The result will be  a ???.

The parameter type is a System.String.
  
You can also create a generic method with out parameters as follows:


 

public void MyGenericMethod<T>()

{

    T x;

    Console.WriteLine("The type of x is  {0}", typeof(T));

}
 
Here we see the method in use:
 
MyGenericMethod<int>();
 
The result will be:


The type of x is a System.Int32.
 
MyGenericMethod<string>();
 
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: 
 

//a generic method that take two parameters

public void MyGenericMethod<T>(T x, T y)

{

    ......

}

......

 

string x, y;

//the compiler here will infer the type parameter

MyGenericMethod(x, y)


In the case of a generic method that doesn't take parameters

//a generic method that doesn't take parameters

public void MyGenericMethod<T>()

{

    ......

}

 

//you must supply the type parameter

MyGenericMethod<string>();

 

//you will have a compiler error here

MyGenericMethod();
 
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.

Part I - Part II

COMMENT USING

Trending up