Le support de la généricité par C#2 par Patrick Smacchia

 

Pré-requis. 1

Introduction. 1

Un problème de C#1 et sa résolution grâce aux types génériques de C#2. 1

Le problème du typage des éléments d’une collection en C#1. 1

Résolution élégante du problème à l’aide d’une classe générique de C#2. 2

Vue d’ensemble de la généricité de C#2. 2

Possibilité pour un type d’être génériques sur plusieurs types. 2

Types génériques ouverts et fermés. 2

La généricité de .NET vs. le mécanisme de templates de C++. 2

Visibilité d’un type générique. 3

Structures et interfaces génériques. 3

Possibilité de créer des alias sur le nom d’un type générique fermé. 3

Possibilité de contraindre un type paramètre. 3

La contrainte du constructeur par défaut 3

Contraintes de dérivation. 3

La contrainte type valeur/type référence. 4

Les contraintes qui vont manquer dans C#2. 4

Les membres d'un type générique. 4

Surcharge de méthode. 4

Les champs statiques. 4

Les méthodes statiques. 5

Le constructeur statique. 5

Surcharge des opérateurs. 5

Les types encapsulés. 5

Les opérateurs et les types génériques. 5

Utilisation des opérateurs d’égalité, d’inégalité et de comparaison avec une instance d’un type paramètre. 5

L’opérateur typeof 5

Les mots clés params et lock. 6

L’opérateur default 6

Le transtypage (casting) et la généricité. 6

Les règles de base. 6

Transtypage entre tableaux. 6

Les opérateurs is et as. 6

L’héritage et la généricité. 6

Les différents cas. 6

Surcharge de méthodes virtuelles d’un type générique. 6

Les méthodes génériques. 7

Introduction. 7

Méthodes génériques et contraintes. 7

Méthodes virtuelles génériques. 7

Inférence des types paramètres selon les types des paramètres d’une méthode générique. 7

Ambiguïté dans la grammaire de C#2. 7

Les délégués, les évènements et la généricité. 8

Introduction. 8

Délégués génériques et méthodes génériques. 8

Contravariance, covariance, délégués et généricité. 8

Evènements et délégués génériques. 8

Réflexion, attribut, IL et généricité. 8

Evolution de la classe System.Type. 8

Evolution des classes System.Reflection.MethodBase et System.Reflection.MethodInfo. 9

Les attributs et la généricité. 10

La généricité et le langage IL. 10

La généricité et le framework .NET. 10

La sérialisation et la généricité. 10

.NET Remoting et la généricité. 10

Les collections et la généricité. 10

Les domaines ne supportant pas la généricité. 11

Conclusion. 11

 

Pré-requis

Une bonne connaissance générale de C#1.

Introduction

Depuis deux ans  déjà, DNG s’intéresse de près au support de la généricité par .NET2, basé sur les travaux des chercheurs du laboratoire MS Research Don Syme et Andrew Kennedy. Rien d’étonnant à cela quand on sait que cette caractéristique est considérée par beaucoup comme l’évolution centrale de la version 2 de .NET (Bill Gates a veillé personnellement à ce que le support des génériques soit prêt pour cette version).

J’ai pu m’apercevoir lors de la rédaction du présent article que l’implémentation des génériques n’est pas encore stabilisée à 100%. Les différentes sources d’information (articles, blogs, newsgroups, ouvrage The C# programming language) sont parfois contradictoires et certains résultats obtenus par mes propres tests laissent à penser qu’il reste une poignée de cas à améliorer. En bref, tous ce qui va être dit ici n’est valable que pour la version Beta1 de .NET (build 2.0.40607) et sera rigoureusement vérifié au cas où ce contenu se transformerait en un chapitre un jour...

Dans cet article, nous allons examiner à la loupe la plupart des implications du support de la généricité, au niveau du langage C#2, du CLR et du Framework. Mais avant cela commençons par une petite introduction à la généricité.

Un problème de C#1 et sa résolution grâce aux types génériques de C#2

Le problème du typage des éléments d’une collection en C#1

Supposons que nous avons à implémenter une classe Stack (pile en français) qui permet d’empiler et de dépiler des éléments. Pour simplifier, nous considérons que la pile ne peut contenir plus qu’un certains nombre d’éléments ce qui nous permet d’utiliser en interne un tableau C#. Voici une implémentation de la classe Stack qui satisfait ces contraintes :

class Stack{

   private object[] m_ItemsArray;

   private int m_Index = 0;

   public const int MAX_SIZE = 100;

   public Stack(){ m_ItemsArray = new object[MAX_SIZE]; }

   public Object Pop(){

      if (m_Index ==0 )

         throw new InvalidOperationException("Impossible de dépiler un élément d'une pile vide.");

      return m_ItemsArray[--m_Index];

   }

   public void Push(Object item) {

      if(m_Index == MAX_SIZE)

         throw new StackOverflowException("Impossible d'empiler un élément sur une pile pleine.");  

      m_ItemsArray[m_Index++] = item;

   }

}

Cette implémentation souffre de trois défauts majeurs.

Premièrement, les clients de la classe Stack doivent transtyper explicitement tout élément obtenu à partir de la pile. Par exemple :

...

Stack stack = new Stack();

stack.Push(1234);

int number = (int)stack.Pop();