Generics

What Are Generics?

Generics allow you to realize type safety at compile time. They allow you to create a data structure without committing to a specific data type. When the data structure is used, however, the compiler makes sure that the types used with it are consistent for type safety. Generics provide type safety, but without any loss of performance or code bloat. While they are similar to templates in C++ in this regard, they are very different in their implementation.

Using Generics Collections

The System.Collections.Generics namespace contains the generics collections

Y Generics

ex:

List<int> aList = new List<int>();
aList.Add(3);
aList.Add(4);
aList.Add(5.0);
int total = 0;
foreach(int val in aList)
{
total = total + val;
}
This will cause an compiler error bcoz not able to cast the double5.0 to integer
But the same was handled as error at runtime while using arraylist.

CLR Support for Generics



Generics is not a mere language-level feature. The .NET CLR recognizes generics. In
that regard, the use of generics is a first-class feature in .NET. For each type of parameter used for
a generic, a class is not rolled out in the Microsoft Intermediate Language (MSIL). In
other words, your assembly contains only one definition of your parameterized data
structure or class, irrespective of how many different types are used for that parameterized
type. For instance, if you define a generic type MyList, only one definition of that
type is present in MSIL. When the program executes, different classes are dynamically created,
one for each type for the parameterized type. If you use MyList and
MyList, then two classes are created on the fly when your program executes.



Writing a generic class




//MyList.cs
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace CLRSupportExample
{
public class MyList
{
private static int objCount = 0;

public MyList()
{
objCount++;
}

public int Count
{
get
{
return objCount;
}
}
}
}

//Program.cs
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace CLRSupportExample
{
class SampleClass {}

class Program
{
static void Main(string[] args)
{
MyList myIntList = new MyList();
MyList myIntList2 = new MyList();

MyList myDoubleList
= new MyList();

MyList mySampleList
= new MyList();

Console.WriteLine(myIntList.Count);
Console.WriteLine(myIntList2.Count);
Console.WriteLine(myDoubleList.Count);
Console.WriteLine(mySampleList.Count);
Console.WriteLine(
new MyList().Count);

Console.ReadLine();
}
}
}


I have created a generic class named MyList. To parameterize it, I simply inserted an angle
bracket. The T within <> represents the actual type that will be specified when the
class is used. Within the MyList class, I have a static field, objCount. I am incrementing this
within the constructor so I can find out how many objects of that type are created by the
user of my class. The Count property returns the number of instances of the same type as
the instance on which it is called.



In the Main() method, I am creating two instances of MyList, one instance of
MyList, and two instances of MyList, where
SampleClass is a
class I have defined. The question is: what will be the value of Count? That is, what is the
output from the above program? Go ahead and think on this and try to answer this
question before you read further.



Have you worked the above question? Did you get the following answer?




2
2
1
1
2

Generics Methods



In addition to having generic classes, you may also have generic methods. Generic methods
may be part of any class. Let's look at Example 4:



Example 4. A generic method




public class Program
{
public static void Copy(List source, List destination)
{
foreach (T obj in source)
{
destination.Add(obj);
}
}

static void Main(string[] args)
{
List lst1 = new List();
lst1.Add(2);
lst1.Add(4);

List lst2 = new List();
Copy(lst1, lst2);
Console.WriteLine(lst2.Count);
}
}


The Copy() method is a generic method that works with the parameterized type T.
When Copy() is invoked in Main(), the compiler figures out the specific
type to use, based on the arguments presented to the Copy() method.



Unbounded Type Parameters



If you create generics data structures or classes, like MyList in Example 3, there are no
restrictions on what type the parametric type you may use for the parameteric type. This
leads to some limitations, however. For example, you are not allowed to use
==, !=, or < on instances of the parametric type:




if (obj1 == obj2) …


The implementation of operators such as == and != are different for value types and
reference types. The behavior of the code may not be easier to understand if these were
allowed arbitrarily. Another restriction is the use of default constructor. For instance, if
you write new T(), you will get a compilation error, because not all classes have a
no-parameter constructor. What if you do want to create an object using new T(), or you
want to use operators such as == and !=? You can, but first you have to constraint the type
that can be used for the parameterized type. Let's look at how to do that.



Constraints and Their Benefits



A generic class allows you to write your class without committing to any type, yet allows
the user of your class, later on, to indicate the specific type to be used. While this
gives greater flexibility by placing some constraints on the types that may be used for
the parameterized type, you gain some control in writing your class. Let's look at an example:



Example 5. The need for constraints: code that will not compile




public static T Max(T op1, T op2)
{
if (op1.CompareTo(op2) <>


The code in Example 5 will produce a compilation error:




Error 1 'T' does not contain a definition for 'CompareTo'


Assume I need the type to support the CompareTo() method. I can specify
this by using the constraint that the type specified for the parameterized type must implement the
IComparable interface. Example 6 has the code:



Example 6. Specifying a constraint




public static T Max(T op1, T op2) where T : IComparable
{
if (op1.CompareTo(op2) <>


In Example 6, I have specified the constraint that the type used for
parameterized type must inherit from (implement) IComparable.
The following constraints may be used:




where T : struct type must be a value type (a struct)
where T : class type must be reference type (a class)
where T : new() type must have a no-parameter constructor
where T : class_name type may be either class_name or one of its
sub-classes (or is below class_name
in the inheritance hierarchy)
where T : interface_name type must implement the specified interface


You may specify a combination of constraints, as in: where T : IComparable, new().
This says that the type for the parameterized type must implement the
IComparable interface and must have a no-parameter constructor.



Inheritance and Generics



A generic class that uses parameterized types, like MyClass1, is called
an open-constructed generic. A generic class that uses no parameterized types,
like MyClass1, is called a closed-constructed generic.



You may derive from a closed-constructed generic; that is, you may inherit a class
named MyClass2 from another class named MyClass1, as in:




public class MyClass2 : MyClass1


You may derive from an open-constructed generic, provided the type is parameterized.
For example:




public class MyClass2 : MyClass2


is valid, but




public class MyClass2 : MyClass2


is not valid, where Y is a parameterized type. Non-generic classes may derive from closed-constructed
generic classes, but not from open-constructed generic classes. That is,




public class MyClass : MyClass1


is valid, but




public class MyClass : MyClass1


is not.


Generics' Limitations

We have seen the power of generics so far in this article. Are there any limitations? There is one significant limitation, which I hope Microsoft addresses. In expressing constraints, we can specify that the parameter type must inherit from a class. How about specifying that the parameter must be a base class of some class? Why do we need that?

In Example 4, I showed you a Copy() method that copied contents of a source List to a destination list. I can use it as follows:

List appleList1 = new List(); List appleList2 = new List(); … Copy(appleList1, appleList2);

However, what if I want to copy apples from one list into a list of Fruits (where Apple inherits from Fruit). Most certainly, a list of Fruits can hold Apples. So I want to write:

List appleList1 = new List(); List fruitsList2 = new List(); … Copy(appleList1, fruitsList2);

This will not compile. You will get an error:

Error 1 The type arguments for method 'TestApp.Program.Copy(System.Collections.Generic.List, System.Collections.Generic.List)' cannot be inferred from the usage.

The compiler, based on the call arguments, is not able to decide what T should be. What I really want to say is that the Copy should accept a List of some type as the first parameter, and a ListList of its base type as the second parameter. of the same type or a

Even though there is no way to say that a type must be a base type of another, you can get around this limitation by still using the constraints. Here is how:

public static void Copy(List source, List destination) where T : E

Here I have specified that the type T must be the same type as, or a sub-type of, E. We got lucky with this. Why? Both T and E are being defined here. We were able to specify the constraint (though the C# specification discourages using E to define the constraint of T when E is being defined as well).

Consider the following example, however:

public class MyList { public void CopyTo(MyList destination) { //… } }

I should be able to call CopyTo:

MyList appleList = new MyList(); MyList appleList2 = new MyList(); //… appleList.CopyTo(appleList2);

I must also be able to do this:

MyList appleList = new MyList(); MyList fruitList2 = new MyList(); //… appleList.CopyTo(fruitList2);

This, of course, will not work. How can we fix this? We need to say that the argument to CopyTo()MyList of some type or MyList of the base type of that type. However, the constraints do not allow us to specify the base type. How about the following? can be either

public void CopyTo(MyList destination) where T : E

Sorry, this does not work. It gives a compilation error that:

Error 1 'TestApp.MyList.CopyTo()' does not define type parameter 'T'

Of course, you may write the code to accept MyList of any arbitrary type and then within your code, you may verify that the type is one of acceptable type. However, this pushes the checking to runtime, losing the benefit of compile-time type safety.

Conclusion

Generics in .NET 2.0 are very powerful. They allow you to write code without committing to a particular type, yet your code can enjoy type safety. Generics are implemented in such a way as to provide good performance and avoid code bloat. While there is the drawback of constraints' inability to specify that a type must be a base type of another type, the constraints mechanism gives you the flexibility to write code with a greater degree of freedom than sticking with the least-common-denominator capability of all types.

0 comments:

Listen to nirmaln - sakara - playlist audio songs at MusicMazaa.com