Vous êtes sur la page 1sur 6

Introduction

For quite sometime now, I am involved in the development of application software using the
.NET Framework version 1.1. But there was one thing that .NET 1.1 really lacked. The support
for something like 'Templates' found in the good old(?) C++. The support for the concept of type
parameters, which makes it possible to design classes which take in a generic type and determine
the actual type later on.

This means that by using a generic type parameter T, you can write a single class MyList<T> and
the client code can use it as MyList<int>, MyList<string> or MyList<MyClass> without any
risk of runtime casts or boxing operations.

My dear friends let me introduce you to the concept of 'Generics', which is included in .NET
Framework version 2.0, and which can be considered very close to Templates in C++.

Version 2.0 of the .NET Framework introduces a new namespace viz.


System.Collections.Generic, which contains the classes that support this concept, like the
List, Queue, Stack, LinkedList and many more which you can use effectively in your
programs.

Advantages of Generics
In the earlier versions, before .NET 2.0, generalization was accomplished by casting types to and
from the universal base type System.Object. Generics provide a solution to this limitation in the
common language runtime and the C# language. This limitation can be demonstrated with the
help of the ArrayList collection class from the .NET Framework base class library. ArrayList
is a highly convenient collection class that can be used without any modifications to store any
reference or value type.

// The .NET Framework 1.1 way of creating a list

ArrayList list1 = new ArrayList();


list1.Add(3);
list1.Add(105);
//...

ArrayList list2 = new ArrayList();


list2.Add("First item.");
list2.Add("Second item");
//...

But this convenience comes at a cost. Any reference or value type that is added to an ArrayList
is implicitly typecast to System.Object. If the items are value types, they must be boxed when
added to the list, and unboxed when they are retrieved. The casting, boxing and unboxing
operations degrade performance; the effect of boxing and unboxing can be quite significant in
scenarios where you must iterate through large collections.
So, what we need is, flexibility of the ArrayList but it should be more efficient and should
provide some ways of type checking by the compiler, without sacrificing on the reusability of
that class with different data types. An ArrayList with a type parameter? That is precisely what
generics provide. Generics would eliminate the need for all items to be type cast to Object and
would also enable the compiler to do some type checking.

In the generic List<T> collection, in System.Collections.Generic namespace, the same


operation of adding items to the collection looks like this:

Collapse
// The .NET Framework 2.0 way of creating a list

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


list1.Add(3); // No boxing, no casting

list1.Add("First item"); // Compile-time error

For the client code, the only added syntax with List<T> compared to ArrayList is the type
argument in the declaration and in instantiation. In return for this slightly greater coding
complexity, you can create a list that is not only safer than ArrayList, but also significantly
faster, especially when the list items are value types.

Generic Classes
Generic classes encapsulate operations that are not specific to any particular data type. The most
common use for generic classes is with the collections like linked lists, hash tables, stacks,
queues, trees and so on where operations such as adding and removing items from the collection
are performed in more or less the same way regardless of the type of the data being stored.

public class Node <T>


{
T head;
T next;
}

Here, T is the type parameter. We can pass any data type as parameter.

This class can be instantiated like this:

Node<string> node = new Node<string>();

This will tell the compiler that the properties, head and next are of type string. Instead of
string, you can substitute this with any data type.

Generic Methods
A generic method is a method that is declared with a type parameter.
void Swap<T>( ref T left, ref T right)
{
T temp;
temp = left;
left = right;
right = temp;
}

The following code example shows how to call the above method:

int a = 1;
int b = 2;
Swap <int> (a, b);

You can also omit the type parameter because the compiler will automatically identify it for you.
The following is also a valid call to the same method:

Swap (a, b);

Write your own Generic class


The following example demonstrates how you can write your own generic classes.

The example shown below is a simple generic linked list class for demonstration purpose:

using System;
using System.Collections.Generic;
public class MyList<T> //type parameter T in angle brackets

{
private Node head;

// The nested class is also generic on T.

private class Node


{
private Node next;
//T as private member data type:

private T data;
//T used in non-generic constructor:

public Node(T t)
{
next = null;
data = t;
}

public Node Next


{
get { return next; }
set { next = value; }
}

//T as return type of property:

public T Data
{
get { return data; }
set { data = value; }
}
}

public MyList()
{
head = null;
}

//T as method parameter type:

public void AddHead(T t)


{
Node n = new Node(t);
n.Next = head;
head = n;
}

public IEnumerator<T> GetEnumerator()


{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}

Notice the declaration of the above class :

public class MyList<T>

T is the parameter type. Throughout the above code, the data type for the Node is T rather than
any specific types like int or string or any other class. This gives flexibility to the programmer
to use this class with any data type he wishes to use.

The following code example shows how the client code uses the generic MyList<T> class to
create a list of integers. Simply by changing the type argument, the code below can be easily
modified to create lists of strings or any other custom type:

class Program
{
static void Main(string[] args)
{
//int is the type argument.

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

for (int x = 0; x < 10; x++)


list.AddHead(x);

foreach (int i in list)


Console.WriteLine(i);

Console.WriteLine("Done");
}
}

Okay. I think you have got a hang of the generics by now, right? Anybody who has worked with
templates in C++ would find this almost similar.

How generics are handled by the .NET runtime


When a generic type or method is compiled into MSIL, it contains metadata that identifies it as
having type parameters. How this MSIL which contains generic type is used is different based on
whether or not the supplied type parameter is a value or reference type.

When a generic type is first constructed with a value type as parameter, the runtime creates a
specialized generic type with the supplied parameter or parameters substituted in the appropriate
places in the MSIL. Specialized generic types are created once for each of the unique value type
used as parameter.

For example, suppose your program code declared a Stack constructed of integers, like this:

Stack<int> stack;

At this point, the runtime generates a specialized version of the Stack class with the integer
substituted appropriately as its parameter. Now, whenever your program code uses a stack of
integers, the runtime reuses the generated specialized Stack class. In the following example, two
instances of a stack of integers are created, and they share a single instance of the Stack<int>
code:

Stack<int> stackOne = new Stack<int>();


Stack<int> stackTwo = new Stack<int>();

However, if at another point in your program code another Stack class is created but with a
different value type such as a long or a user-defined structure as its parameter, then the runtime
generates another version of the generic type, this time substituting a long in the appropriate
places in the MSIL. Conversions are no longer necessary because each specialized generic class
natively contains the value type.
Generics work a bit differently for reference types. The first time a generic type is constructed
with any reference type, the runtime creates a specialized generic type with the object references
substituted for the parameters in the MSIL. Then, each time a constructed type is instantiated
with a reference type as its parameter, regardless of its type, the runtime reuses the previously
created specialized version of the generic type. This is possible because all references are the
same size.

For example, suppose you had two reference types, a Customer class and an Order class, and
that you created a stack of Customer types:

Stack<Customer> customers;

At this point, the runtime generates a specialized version of the Stack class that, instead of
storing data, stores object references that will be filled in later. Suppose the next line of code
creates a stack of another reference type, called Order:

Stack<Order> orders = new Stack<Order>();

Unlike the value types, another specialized version of the Stack class is not created for the
Order type. Rather, an instance of the specialized version of the Stack class is created and the
orders variable is set to reference it. Suppose you then encountered a line of code to create a
stack of a Customer type:

customers = new Stack<Customer>();

As with the previous use of the Stack class created with the Order type, another instance of the
specialized Stack class is created, and the pointers contained therein are set to reference an area
of memory the size of a Customer type. The C# implementation of generics greatly reduces code
bloat by reducing the number of specialized classes created by the compiler for generic classes of
reference types to just one.

How does C# generics differ from C++ templates


C# Generics and C++ templates are both language features that provide support for
parameterized types. However, there are many differences between the two. At the syntax level,
the C# generics are a simpler approach to parameterized types without any of the complexities of
C++ templates. In addition, C# does not attempt to provide all the functionality that C++
templates provide.

At the implementation level, the primary difference is that the C# generic type substitutions are
performed at runtime and generic type information is preserved for the instantiated objects

Vous aimerez peut-être aussi