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