Programmation C sharp/Types génériques

Un type générique est en fait un type non spécifié à l'implémentation d'une méthode ou d'une classe. Le type sera déterminé lors de l'utilisation de cette méthode ou classe. En fait, pour chaque type différent utilisé, le compilateur génèrera une nouvelle version de la méthode ou de la classe.

Programmation C#
Programmation C#
Modifier ce modèle

Les types génériques sont utilisés pour effectuer un même traitement sur différents types de données.

Syntaxe modifier

Les types génériques sont identifiés par un nom, et spécifiés entre les signes < et > placés juste après le nom de la méthode ou la classe.

Exemple de méthode modifier

Soit une méthode statique retournant la valeur maximale entre deux valeurs passées en paramètre :

public static T max<T>(T a, T b) // Retourne la valeur maximale
{
    return a > b ? a : b ;
}
 
int entier = max ( 10 , 22 );
double vmax = max ( 3.14 , 1.618 );

Le compilateur détermine le type utilisé pour T d'après les valeurs des arguments. Dans le cas précédent, il génère deux versions surchargées de la méthode statique :

  • public static int max(int a, int b)
  • public static double max(double a, double b)

Exemple de classe modifier

La syntaxe est similaire. Soit une classe gérant une structure en arbre de valeurs de type quelconque :

public class Arbre<T>
{
    public T valeur;
    private Arbre<T> _gauche, _droite;

    public Arbre<T> ArbreGauche
    {
        get { return _gauche; }
    }
}

L'utilisation de cette classe exige de spécifier explicitement le type utilisé :

Arbre<int> ArbreDesEntiers = new Arbre<int>();
ArbreDesEntiers.valeur = 100;

Exemple de structure modifier

Les types génériques sont également utilisables avec les structures :

public struct Taille<T>
{
    public T largeur, hauteur;
}

Plusieurs types génériques modifier

Il est possible d'utiliser plusieurs types génériques pour une méthode ou une classe :

public static void affiche<T,U>(T a, U b) // Affiche a et b
{
    Console.WriteLine("A vaut {0}, et B vaut {1}", a, b);
}
 
affiche(10,3.1415926);

Contraintes sur les types génériques modifier

Il est possible de restreindre les types utilisables à ceux qui implémentent une ou plusieurs interfaces.

Syntaxe modifier

Pour chaque type à contraindre, il faut ajouter une clause where :

where type : liste_des_interfaces

Exemple :

public class TableauTriable<T>
    where T : IComparable
{
    //...
}

Il est possible d'utiliser class ou struct pour limiter le type à une classe ou une structure.

Exemple :

public class TableauTriable<T>
    where T : struct
{
    //...
}

Il est également possible d'ajouter des contraintes sur les constructeurs du type générique :

public class TableauTriable<T>
    where T : new()   // T doit avoir un constructeur sans paramètre
{
    public T Creer()
    {
        return new T(); // appel au constructeur de T
    }
}

Opérateur default modifier

L'opérateur default retourne la valeur par défaut du type générique spécifié. Il s'agit de la valeur quand une variable de ce type n'est pas initialisée (0 pour les nombres, null pour les types références).

Exemple:

public T maxAbsolu(T a,T b)
{
    if (a==b) return default(T);
    else return a>b ? a : b;
}

Alias modifier

Il est possible de définir un alias d'une classe générique spécifique, en utilisant le mot clé using :

using Entiers = TableauTriable<int>;
Entiers entiers = new Entiers();

Équivaut à :

TableauTriable<int> entiers = new TableauTriable<int>();