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.What Are Generics?
Using Generics Collections
The System.Collections.Generics namespace contains the generics collections
Y Generics
ex: Generics is not a mere language-level feature. The .NET CLR recognizes generics. In Writing a generic class I have created a generic class named In the Have you worked the above question? Did you get the following answer? In addition to having generic classes, you may also have generic methods. Generic methods Example 4. A generic method The If you create generics data structures or classes, like The implementation of operators such as A generic class allows you to write your class without committing to any type, yet allows Example 5. The need for constraints: code that will not compile The code in Example 5 will produce a compilation error: Assume I need the type to support the Example 6. Specifying a constraint In Example 6, I have specified the constraint that the type used for You may specify a combination of constraints, as in: A generic class that uses parameterized types, like You may derive from a closed-constructed generic; that is, you may inherit a class You may derive from an open-constructed generic, provided the type is parameterized. is valid, but is not valid, where is valid, but is not.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
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 andMyList, then two classes are created on the fly when your program executes.
//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
MyList
MyList
= new MyList
MyList
= new MyList
Console.WriteLine(myIntList.Count);
Console.WriteLine(myIntList2.Count);
Console.WriteLine(myDoubleList.Count);
Console.WriteLine(mySampleList.Count);
Console.WriteLine(
new MyList
Console.ReadLine();
}
}
}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.Main() method, I am creating two instances of MyList, one instance ofMyList, and two instances of MyList, whereSampleClass 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.
2
2
1
1
2Generics Methods
may be part of any class. Let's look at Example 4:
public class Program
{
public static void Copy
{
foreach (T obj in source)
{
destination.Add(obj);
}
}
static void Main(string[] args)
{
List
lst1.Add(2);
lst1.Add(4);
List
Copy(lst1, lst2);
Console.WriteLine(lst2.Count);
}
}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
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) …== 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
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:
public static T Max
{
if (op1.CompareTo(op2) <>
Error 1 'T' does not contain a definition for 'CompareTo'CompareTo() method. I can specify
this by using the constraint that the type specified for the parameterized type must implement theIComparable interface. Example 6 has the code:
public static T Max
{
if (op1.CompareTo(op2) <>
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 interfacewhere T : IComparable, new().
This says that the type for the parameterized type must implement theIComparable interface and must have a no-parameter constructor.Inheritance and Generics
MyClass1, is called
an open-constructed generic. A generic class that uses no parameterized types,
like MyClass1, is called a closed-constructed generic.
named MyClass2 from another class named MyClass1, as in:
public class MyClass2
For example:
public class MyClass2
public class MyClass2Y 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
public class MyClass : MyClass1
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:
Post a Comment