FootNotes on C# Generics

 

To declare a list of  objects  of type T

List<T> list=new List<T>();

Here T is a parametric Type. You can use any data type  or class in place of the T.

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

Here, List is a collection of objects of type integer.

Now if you write..

list.add(1);

list.add(2);

It will execute successfully .But if you just write

list.add(3.0);

It will throw a runtime exception because 3.0 is not of integer type and the generic list has been declared as type integer.

This is how you declare a generic type class:

public class FirstList<T>

{

}

So T  acts as a constructor parameter, which defines the type for the generic. Some interesting facts about the execution cycle of generics should be discussed here.

If you declare and instantiate MyList Like bellow:

FirstList<int> list1=new FirstList<int>();

FirstList<int> list2=new FirstList<int>();

Firstlist<double>  list3=new FirstList<double>();

Normal situation suggests that we have only one class there with 3 instances. But if we dig deep we will find out that there are two classes here . FirstList[System.Int32]   which has 2 instances and FirstList[System.Double], which has one instance. Interesting thing is that , in IL level, there is only one class FirstList<T>.So where does these 2 classes come from? When are they created. Interesting to know that the instances of these two types of classes are created in runtime by the CLR when the code is loaded in the memory. That gives the system a great performance overhead. So the classes are only created when they are needed, which is a very efficient way of programming.

Now lets talk about Generic methods.

public T Max<T>(T val1, T val2) where T : IComparable  {

    T FinalVal = val2;

    if (val2.CompareTo(val1) < 0)

        FinalVal = val1;

    return FinalVal;

   }

The method here is called Max and it has become a parameterized type. It accepts a type parameter, which is the signature for any Generic method. So whatever type you pass on in the type parameter, can be referenced within the method. Note that as it is declared as Type T, the return type of the function will be also of that parameterized type. Look at the second part of the function declaration. The ‘where’ section shows how to implement constraints on the Generic methods. By constraint here, we say the compiler that, the parametric type T has to implement the IComparable interface.

“A derived class inherits a base class, but a collection of derived class, doesn’t inherit a collection of base class”.

Lets say we have 3 classes like these:

class Furniture{}

class Table{}:Furniture{}

class Chair{}:Furniture{}

Suppose we have a method in Furniture class.

static void Make(Furniture f){}

Here if we pass a table or chair object, that would also compile successfully.

Make(Table t);

Make(Chair c);

If we override the Make method in the derived class Table as:

public override Make(Furniture F)

{

         base.Make(F);

}

There is a problem, we can even pass an object  of class Chair  in this method. So a Table class can even make a Chair.Many programmers would not want that to happen. Here comes the use of generics.

“A derived class inherits a base class, but a collection of derived class, doesn’t inherit a collection of base class”.

Lets  say we have a method

static void Make(FirstList<Furniture>  furniture){}

So if we invoke the method as

Make(new FirstList<Furniture>  furniture);

It would compile successfully. But if we try something like this

Make(new FirstList<Table> table);

It will raise a compile time error. Why? Because a object of Table class do inherit  base class Furniture, but a list or collection of Table objects do not inherit a list or collection of Furniture class.

Lets  see this example:

class Furniture

{

public  virtual  void Paint(FirstList<Furniture> furnitures ) {}

}

public class  Table:Furniture

{

public  override  void Paint(FirstList<Furniture> furnitures ) {}

}

public class  Chair:Furniture

{

public  override  void Paint(FirstList<Furniture> furnitures ) {}

}

 

Now if we test the new classes.

class  Program

{

                Static void Main(string[] args)

                {

Table table=new Table();

Table.Paint(new FirstList<Furniture>());

Table.Paint(new FirstList<Table>());

}

}

If we execute this code, the first 2 lines will execute, but it would raise error in the 3rd line. Why? The Table class can’t even Paint its on instance. Table is a child class of Furniture and there might be some situations where we would like that the table class has  the capability to paint Chair objects also. How would  we establish that?

Lets make some improvements in our classes. Make the methods generic methods. Lets change all the 3 Paint methods with this new one.

public  override  void Paint<T>(FirstList<T> furnitures ) {}

So now we will see if we execute

Table.Paint(new FirstList<Table>());

Table.Paint(new FirstLIst<Chair>());

Both these lines will execute successfully. Oh, good work? Well, isn’t there a glitch? Let us run this..

Table.Paint(new FIrstList<Program>());

Oops, it is executing without any error. Should we let that happen? Shouldn’t we make sure that it can only pain derived classes of the Furniture. So lets do it.

What we have to do here is to put a constraint in the method declaration of the base class that is Furniture. Lets do it like this.

public  override  void Paint<T>(FirstList<T> furnitures ) where T : Furniture

 {}

So we are making sure here that  any type passed in the parameterized type variable T is indeed a derived class of base class that is Furniture here. Remember that putting this constraint only in the base class would be enough, we don’t have to repeat it in the child classes again.

 

Now lets make some changes in our generic class.

public class FirstList<T>

{

public  void  Insert(T obj)

{

}

public  void  Push (()

{

                Insert(new T());

}

}

 

Now if we compile this, we would get an error. In the line add(new T()). That is because T is a generic  parameter Type and remember that all classes do not have a parameterless constructor. So to make it work we will have to add a constraint in the class declaration.

public class FirstList<T> where T : new()

{

………...

}

So it defines that any type passed as the parameter type in the class would have to have a parameterless constructor.

So if you  modify the class furniture like this

class Furniture

{

public  virtual  void Paint<T>(FirstList<T> furnitures ) {}  where T:  Furniture

}

In this case we would get an error here.

“'T' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'ConsoleApplication1.MyList<T>'”

This is happening because FirstList  as a generic type expects the type parameter to have parameterless constructor. So if we change the generic method declaration as

public  virtual  void Paint<T>(FirstList<T> furnitures ) {}  where T:  Furniture,new()

Our problem will be solved.

 

 Inspired by Presentation on www.dnrTv.com

62 Comments Filed Under [ C# ]