Why Generics-
Type safety ,better performance,Source code protection and Cleaner code
(Type safty and performance are discussed later)
Source code protection: Developers don't need any source code if they are using generic algorithms.
Cleaner Code: as compiler take care of type safety so we need not to type caste the things again and again. so we get a good readable code.
Better performance - Only in case of value type, as it avoid boxing and unboxing.
Type Safety-
static void Main(string[] args)
{
ArrayList list = new ArrayList();
populateList(list);
int temp;
for (int i = 0; i < list.Count; i++)
{
temp = (int)list[i];
Console.WriteLine(temp);
}
}
private static void populateList(ArrayList list)
{
list.Add(1);
list.Add(2);
list.Add("hello"); //No error from compiler
}It will cause runtime error -> Specified cast is not valid
But in case of generics:
List<int> list = new List <int>();
list.Add(1);
list.Add(2);
list.Add("hello"); //Compiler shows error
So at compile time itself we get error in case of type mismatch.
Boxing/UnBoxing-
Boxing is the act of converting a value type to a reference type. If we look at line #2 of the first code sample above, we’re putting an Int32 (a value type) into an ArrayList which stores everything as System.Object (a reference type).
Boxing causes memory allocation on managed heap, which causes more frequent garbage collections, which in turn hurt an application performance.
Unboxing is the act of converting a reference type to a value type (e.g., if we were taking an item out of the ArrayList and having to cast: int num = (int)list[0];).
- Generic Type and inheritance
IList<string> mylist = new List<string>();
IList<int> myintlist = new List<int>();
Both of these types will be derived from whatever type the generic type was derived from. If the generic List<T> is derived from object, it means list<string> and List<int> are also derived from object.
It does not mean that mylist.GetType()==myintlist.GetType() will return you true.
Inheritance in Generics -
There are different kind of inheritance possible -
Points to remember -
In case of (ii) and (iv) all overridden methods will have to define the types in the generic implementation. In other words we can not have any generic class definition which has overridden method and the return type or input parameters are T.
Here is the example -
class A<T> : C
{
T temp;
public A(T t) { temp = t; }
public override string GetT(string t) { return "";}
public void SetT(T t) { temp = t;}
}
class C
{
public C() { }
public virtual String GetT(string a ) { return "C";}
}
In above case we can not have method GetT taking parameter as T or returning type T.
Generic Interface -
Using generic we can define interface that defines methods with generic parameters. Here is the example -
Here is nonGeneric IComparable interface
public class A : IComparable
{
public int myInt = 0;
public int CompareTo(object obj)
{
A a = obj as A;
return this.myInt.CompareTo(a.myInt);
}
}
Here is Generic Comparable interface.
public class B : IComparable<B>
{
public int bint = 0;
public int CompareTo(B other)
{
return this.bint.CompareTo(other.bint);
}
}
If we compare above 2 examples then we see in 2nd case, we dont need any type cast.
Constraints -
While compiling generic code, c# compiler make sure that the code will work for anytype exist today, or introduced later. For example -
private void Min<T>(T o1)
{
Console.WriteLine(o1.ToString());
}
The above code compiles successfully but
private void Min<T>(T o1)
{
Console.WriteLine(o1.CompareTo(o1));
}
Won't compile. As every type will have ToString method available to them, but not every type will have compareTo Method available. Thus CLR provides "constraints" to solve this limitation. We can always define this method like this-
private void Min<T>(T o1) where T: IComparable<T>
{
Console.WriteLine(o1.CompareTo(o1));
}
It not possible to put a constraint on overridden method, when there is not constraint available on parent method. Here is the example-
class A<T>
{
public A() { }
public virtual void Min<T>(T o1)
{
Console.WriteLine(o1.ToString());
}
}
class B<T> : A<T>
{
public B() { }
public override void Min<T>(T o1) where T:ICloneable
{
Console.WriteLine(o1.ToString());
}
}
The above code will give error, because Min MEthod doesn't have any constraint in base class, but we are trying to put a constraint in derived class.
Moreover we can not put constraint using object class, which means following code won't compile-
public override void Min<T>(T o1) where T:object
{
Console.WriteLine(o1.ToString());
}
But its always possible to put constraints of value type or reference type using class/struct as constraints.
Here are some description about different kind of constraints allowed in c# -
Constraint : Description
where T : struct With a struct constraint, type T must be a value type.
where T : class The class constraint indicates that type T must be a reference type.
where T : IMyInterface where T : IMyInterface specifies that type T is required to implement interface IMyInterface.
where T : MyClass where T : MyClass specifies that type T is required to derive from base class MyClass.
where T : new() where T : new() is a constructor constraint and specifies that type T must have a default constructor.
where T1 : T2 With constraints it is also possible to specify that type T1 derives from a generic type T2. This constraint is known as naked type constraint.
Code Explosion-
When we use a generic type and specify the type arguments, CLR defines new type object for every type we mention. For example -
IList<string> mylist = new List<string>();
IList<int> myintlist = new List<int>();
For above 2 line, CLR will create 2 different types, list of string and list of int. It means for every method/type combincation CLR will generate native code, which is called as code explosion. This might hurt the performance. In order to overcome this issue, CLR has its own optimization -
Optimization 1 - If one method is called for specific argument then CLR will compile it once and will serve all the instances of this method call from same code generated(in a appdomain).
Optimization 2- CLR compiles the code for reference type only once, as all reference types are just pointers, so List of string and list of Datetime will share the same compiled code.(This is not applicable for Value types)
Setting a Default Value-
If we want to set a default value of any generic type then we need to use "default" keyword, We can not directly assign null, as value type don't have null.
Which means following code will give error -
private static setDefault<T>(T o1)
{
T temp = null; //error
}
To fix the above code use default-
private static setDefault<T>(T o1)
{
T temp = default(T);
}
temp will be null if its ref type and will be all bits zero if its a value type.
Comparison of generic type-
private static CompareResult<T>(T o1, T o2)
{
if(o1 == o2) // error
}
Above code will give error as not every value type has == operator.
Open and closed types-
Type with the generic type parameters(no definition of T) is called open type and CLR does not allow an instance of any open type. If actual data type are passed for all type arguments, it is called as closed type.
Ex:
Object o;
Open type :
Type t = typeof(Dictionary<,>);
o = createinstance(t); //error
Closed Type:
//DictionaryString is defined like this Dictionary an open type.
//here we have also defined 2nd parameter so it has became a closed type
Type t = typeof(DictionaryString<GUID>);
o = createInstance(t); //No Error
Static Member-
If a static constructor is defined on generic type, then it will be execute once for every close type.
For example -
There is class which contains static filed x -
public class StaticDemo < T >
{
public static int x;
}
Because of using the class StaticDemo <T> both with a string type and an int type, two sets of static fields exist:
StaticDemo<string>.x = 4;
StaticDemo<int>.x = 5;
Console.WriteLine(StaticDemo<string>.x); // writes 4
Limitations-
Methamatical operators like +, -, * and /, can not be applied on any generic type, because we dont know the type beforehand. Which means its impossible to write methematical algo that works on arbitrary data type.
Type safety ,better performance,Source code protection and Cleaner code
(Type safty and performance are discussed later)
Source code protection: Developers don't need any source code if they are using generic algorithms.
Cleaner Code: as compiler take care of type safety so we need not to type caste the things again and again. so we get a good readable code.
Better performance - Only in case of value type, as it avoid boxing and unboxing.
Type Safety-
static void Main(string[] args)
{
ArrayList list = new ArrayList();
populateList(list);
int temp;
for (int i = 0; i < list.Count; i++)
{
temp = (int)list[i];
Console.WriteLine(temp);
}
}
private static void populateList(ArrayList list)
{
list.Add(1);
list.Add(2);
list.Add("hello"); //No error from compiler
}It will cause runtime error -> Specified cast is not valid
But in case of generics:
List<int>
list.Add(1);
list.Add(2);
list.Add("hello"); //Compiler shows error
So at compile time itself we get error in case of type mismatch.
Boxing/UnBoxing-
Boxing is the act of converting a value type to a reference type. If we look at line #2 of the first code sample above, we’re putting an Int32 (a value type) into an ArrayList which stores everything as System.Object (a reference type).
Boxing causes memory allocation on managed heap, which causes more frequent garbage collections, which in turn hurt an application performance.
Unboxing is the act of converting a reference type to a value type (e.g., if we were taking an item out of the ArrayList and having to cast: int num = (int)list[0];).
- Generic Type and inheritance
IList<string> mylist = new List<string>();
IList<int> myintlist = new List<int>();
Both of these types will be derived from whatever type the generic type was derived from. If the generic List<T> is derived from object, it means list<string> and List<int> are also derived from object.
It does not mean that mylist.GetType()==myintlist.GetType() will return you true.
Inheritance in Generics -
There are different kind of inheritance possible -
In case of (ii) and (iv) all overridden methods will have to define the types in the generic implementation. In other words we can not have any generic class definition which has overridden method and the return type or input parameters are T.
Here is the example -
class A<T> : C
{
T temp;
public A(T t) { temp = t; }
public override string GetT(string t) { return "";}
public void SetT(T t) { temp = t;}
}
class C
{
public C() { }
public virtual String GetT(string a ) { return "C";}
}
In above case we can not have method GetT taking parameter as T or returning type T.
Generic Interface -
Using generic we can define interface that defines methods with generic parameters. Here is the example -
Here is nonGeneric IComparable interface
public class A : IComparable
{
public int myInt = 0;
public int CompareTo(object obj)
{
A a = obj as A;
return this.myInt.CompareTo(a.myInt);
}
}
Here is Generic Comparable interface.
public class B : IComparable<B>
{
public int bint = 0;
public int CompareTo(B other)
{
return this.bint.CompareTo(other.bint);
}
}
If we compare above 2 examples then we see in 2nd case, we dont need any type cast.
Constraints -
While compiling generic code, c# compiler make sure that the code will work for anytype exist today, or introduced later. For example -
private void Min<T>(T o1)
{
Console.WriteLine(o1.ToString());
}
The above code compiles successfully but
private void Min<T>(T o1)
{
Console.WriteLine(o1.CompareTo(o1));
}
Won't compile. As every type will have ToString method available to them, but not every type will have compareTo Method available. Thus CLR provides "constraints" to solve this limitation. We can always define this method like this-
private void Min<T>(T o1) where T: IComparable<T>
{
Console.WriteLine(o1.CompareTo(o1));
}
It not possible to put a constraint on overridden method, when there is not constraint available on parent method. Here is the example-
class A<T>
{
public A() { }
public virtual void Min<T>(T o1)
{
Console.WriteLine(o1.ToString());
}
}
class B<T> : A<T>
{
public B() { }
public override void Min<T>(T o1) where T:ICloneable
{
Console.WriteLine(o1.ToString());
}
}
The above code will give error, because Min MEthod doesn't have any constraint in base class, but we are trying to put a constraint in derived class.
Moreover we can not put constraint using object class, which means following code won't compile-
public override void Min<T>(T o1) where T:object
{
Console.WriteLine(o1.ToString());
}
But its always possible to put constraints of value type or reference type using class/struct as constraints.
Here are some description about different kind of constraints allowed in c# -
Constraint : Description
where T : struct With a struct constraint, type T must be a value type.
where T : class The class constraint indicates that type T must be a reference type.
where T : IMyInterface where T : IMyInterface specifies that type T is required to implement interface IMyInterface.
where T : MyClass where T : MyClass specifies that type T is required to derive from base class MyClass.
where T : new() where T : new() is a constructor constraint and specifies that type T must have a default constructor.
where T1 : T2 With constraints it is also possible to specify that type T1 derives from a generic type T2. This constraint is known as naked type constraint.
Code Explosion-
When we use a generic type and specify the type arguments, CLR defines new type object for every type we mention. For example -
IList<string> mylist = new List<string>();
IList<int> myintlist = new List<int>();
For above 2 line, CLR will create 2 different types, list of string and list of int. It means for every method/type combincation CLR will generate native code, which is called as code explosion. This might hurt the performance. In order to overcome this issue, CLR has its own optimization -
Optimization 1 - If one method is called for specific argument then CLR will compile it once and will serve all the instances of this method call from same code generated(in a appdomain).
Optimization 2- CLR compiles the code for reference type only once, as all reference types are just pointers, so List of string and list of Datetime will share the same compiled code.(This is not applicable for Value types)
Setting a Default Value-
If we want to set a default value of any generic type then we need to use "default" keyword, We can not directly assign null, as value type don't have null.
Which means following code will give error -
private static setDefault<T>(T o1)
{
T temp = null; //error
}
To fix the above code use default-
private static setDefault<T>(T o1)
{
T temp = default(T);
}
temp will be null if its ref type and will be all bits zero if its a value type.
Comparison of generic type-
private static CompareResult<T>(T o1, T o2)
{
if(o1 == o2) // error
}
Above code will give error as not every value type has == operator.
Open and closed types-
Type with the generic type parameters(no definition of T) is called open type and CLR does not allow an instance of any open type. If actual data type are passed for all type arguments, it is called as closed type.
Ex:
Object o;
Open type :
Type t = typeof(Dictionary<,>);
o = createinstance(t); //error
Closed Type:
//DictionaryString is defined like this Dictionary an open type.
//here we have also defined 2nd parameter so it has became a closed type
Type t = typeof(DictionaryString<GUID>);
o = createInstance(t); //No Error
Static Member-
If a static constructor is defined on generic type, then it will be execute once for every close type.
For example -
There is class which contains static filed x -
public class StaticDemo < T >
{
public static int x;
}
Because of using the class StaticDemo <T> both with a string type and an int type, two sets of static fields exist:
StaticDemo<string>.x = 4;
StaticDemo<int>.x = 5;
Console.WriteLine(StaticDemo<string>.x); // writes 4
Limitations-
Methamatical operators like +, -, * and /, can not be applied on any generic type, because we dont know the type beforehand. Which means its impossible to write methematical algo that works on arbitrary data type.
No comments:
Post a Comment