Partager via


Génériques dans .NET

Les génériques vous permettent d'adapter une méthode, une classe ou une structure au type de données sur lequel elle agit. Par exemple, au lieu d’utiliser la Hashtable classe, qui permet aux clés et aux valeurs d’être de n’importe quel type, vous pouvez utiliser la Dictionary<TKey,TValue> classe générique et spécifier les types autorisés pour la clé et la valeur. Parmi les avantages des génériques, la réutilisation du code et la sécurité des types sont accrues.

Définir et utiliser des génériques

Les génériques sont des classes, des structures, des interfaces et des méthodes qui possèdent des espaces réservés (ou paramètres de type) pour un ou plusieurs des types qu'ils stockent ou utilisent. Une classe de collection générique peut utiliser un paramètre de type comme espace réservé pour le type d’objets qu’il stocke. Les paramètres de type apparaissent comme les types de ses champs et les types de paramètres de ses méthodes. Une méthode générique peut utiliser son paramètre de type comme type de sa valeur de retour ou comme type de l’un de ses paramètres formels.

Le code suivant illustre une définition de classe générique simple.

public class SimpleGenericClass<T>
{
    public T Field;
}
Public Class SimpleGenericClass(Of T)
    Public Field As T

End Class

Lorsque vous créez une instance d’une classe générique, vous spécifiez les types réels à remplacer par les paramètres de type. Cela établit une nouvelle classe générique, appelée classe générique construite, avec vos types choisis substitués partout où les paramètres de type apparaissent. Le résultat est une classe de type sécurisée adaptée à votre choix de types, comme le montre le code suivant.

public static void Main()
{
    SimpleGenericClass<string> g = new SimpleGenericClass<string>();
    g.Field = "A string";
    //...
    Console.WriteLine($"SimpleGenericClass.Field           = \"{g.Field}\"");
    Console.WriteLine($"SimpleGenericClass.Field.GetType() = {g.Field.GetType().FullName}");
}
Public Shared Sub Main()
    Dim g As New SimpleGenericClass(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("SimpleGenericClass.Field           = ""{0}""", g.Field)
    Console.WriteLine("SimpleGenericClass.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

Terminologie

Les termes suivants sont utilisés pour discuter des génériques dans .NET :

  • Une définition de type générique est une classe, une structure ou une déclaration d’interface qui fonctionne comme un modèle, avec des espaces réservés pour les types qu’il peut contenir ou utiliser. Par exemple, la System.Collections.Generic.Dictionary<TKey,TValue> classe peut contenir deux types : clés et valeurs. Étant donné qu’une définition de type générique n’est qu’un modèle, vous ne pouvez pas créer d’instances d’une classe, d’une structure ou d’une interface qui est une définition de type générique.

  • Les paramètres de type générique, ou les paramètres de type, sont les espaces réservés dans une définition de type ou de méthode générique. Le System.Collections.Generic.Dictionary<TKey,TValue> type générique a deux paramètres de type, TKey et TValue, qui représentent les types de ses clés et de ses valeurs.

  • Un type générique construit ou un type construit est le résultat de la spécification de types pour les paramètres de type générique d’une définition de type générique.

  • Un argument de type générique est n’importe quel type qui est remplacé par un paramètre de type générique.

  • Le type générique général inclut à la fois les types construits et les définitions de types génériques.

  • La covariance et la contravariance des paramètres de type générique vous permettent d’utiliser des types génériques construits dont les arguments de type sont plus dérivés (covariance) ou moins dérivés (contravariance) qu’un type construit cible. La covariance et la contravariance sont collectivement appelées variance. Pour plus d’informations, consultez Covariance et contravariance.

  • Les contraintes sont des limites placées sur les paramètres de type générique. Par exemple, vous pouvez limiter un paramètre de type aux types qui implémentent l’interface System.Collections.Generic.IComparer<T> générique pour vous assurer que les instances du type peuvent être ordonnées. Vous pouvez également limiter les paramètres de type aux types qui ont une classe de base particulière, qui ont un constructeur sans paramètre, ou qui sont des types référence ou des types valeur. Les utilisateurs du type générique ne peuvent pas remplacer les arguments de type qui ne répondent pas aux contraintes.

  • Une définition de méthode générique est une méthode avec deux listes de paramètres : une liste de paramètres de type générique et une liste de paramètres formels. Les paramètres de type peuvent apparaître comme type de retour ou comme types des paramètres formels, comme le montre le code suivant.

    T MyGenericMethod<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
    
    Function MyGenericMethod(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
    

    Les méthodes génériques peuvent apparaître sur des types génériques ou non génériques. Il est important de noter qu’une méthode n’est pas générique uniquement parce qu’elle appartient à un type générique, ou même parce qu’elle a des paramètres formels dont les types sont les paramètres génériques du type englobant. Une méthode est générique uniquement si elle possède sa propre liste de paramètres de type. Dans le code suivant, seule la méthode G est générique.

    class A
    {
        T G<T>(T arg)
        {
            T temp = arg;
            //...
            return temp;
        }
    }
    class MyGenericClass<T>
    {
        T M(T arg)
        {
            T temp = arg;
            //...
            return temp;
        }
    }
    
    Class A
        Function G(Of T)(ByVal arg As T) As T
            Dim temp As T = arg
            '...
            Return temp
        End Function
    End Class
    Class MyGenericClass(Of T)
        Function M(ByVal arg As T) As T
            Dim temp As T = arg
            '...
            Return temp
        End Function
    End Class
    

Avantages et inconvénients des génériques

Il existe de nombreux avantages à l’utilisation de collections et de délégués génériques :

  • Sécurité des types. Les génériques déplacent le fardeau de la sécurité de type entre vous et le compilateur. Il n’est pas nécessaire d’écrire du code pour tester le type de données correct, car il est appliqué au moment de la compilation. La nécessité de la conversion de type et la possibilité d’erreurs d’exécution sont réduites.

  • Moins de code et un code plus facilement réutilisable. Il n’est pas nécessaire d’hériter d’un type de base et de remplacer des membres. Par exemple, le LinkedList<T> est prêt à être utilisé immédiatement. Par exemple, vous pouvez créer une liste liée de chaînes avec la déclaration de variable suivante :

    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Meilleures performances. Les types de collection génériques fonctionnent généralement mieux pour stocker et manipuler des types de valeur, car il n’est pas nécessaire de boxer les types de valeur.

  • Les délégués génériques activent les rappels sécurisés de type sans avoir à créer plusieurs classes de délégué. Par exemple, le Predicate<T> délégué générique vous permet de créer une méthode qui implémente vos propres critères de recherche pour un type particulier et d’utiliser votre méthode avec des méthodes du Array type comme Find, FindLastet FindAll.

  • Les génériques simplifient le code généré dynamiquement. Lorsque vous utilisez des génériques avec du code généré dynamiquement, vous n’avez pas besoin de générer le type. Cela augmente le nombre de scénarios dans lesquels vous pouvez utiliser des méthodes dynamiques légères au lieu de générer des assemblys entiers. Pour plus d’informations, consultez Guide pratique pour définir et exécuter des méthodes dynamiques et DynamicMethod.

Voici quelques limitations des génériques :

  • Les types génériques peuvent être dérivés de la plupart des classes de base, telles que MarshalByRefObject (et les contraintes peuvent être utilisées pour exiger que les paramètres de type générique dérivent de classes de base comme MarshalByRefObject). Toutefois, .NET ne prend pas en charge les types génériques liés au contexte. Un type générique peut être dérivé de ContextBoundObject, mais essayer de créer une instance de ce type provoque un TypeLoadException.

  • Les énumérations ne peuvent pas avoir de paramètres de type générique. Une énumération peut être générique uniquement accessoirement (par exemple, car elle est imbriquée dans un type générique défini à l’aide de Visual Basic, C# ou C++). Pour plus d’informations, consultez « Énumérations » dans Common Type System.

  • Les méthodes dynamiques légères ne peuvent pas être génériques.

  • Dans Visual Basic, C# et C++, un type imbriqué placé dans un type générique ne peut pas être instancié, sauf si les types ont été affectés aux paramètres de type de tous les types englobants. Une autre façon de dire ceci est qu’en réflexion, un type imbriqué défini à l’aide de ces langages inclut les paramètres de type de tous ses types englobants. Cela permet d’utiliser les paramètres de type des types englobants dans les définitions membres d’un type imbriqué. Pour plus d’informations, consultez « Types imbriqués » dans MakeGenericType.

    Remarque

    Un type imbriqué défini par l’émission de code dans un assembly dynamique ou à l’aide de l' Ilasm.exe (assembleur IL) n’est pas nécessaire pour inclure les paramètres de type de ses types englobants ; toutefois, s’il ne les inclut pas, les paramètres de type ne sont pas dans l’étendue de la classe imbriquée.

    Pour plus d’informations, consultez « Types imbriqués » dans MakeGenericType.

Bibliothèque de classes et support linguistique

.NET fournit un certain nombre de classes de collection génériques dans les espaces de noms suivants :

Les interfaces génériques pour implémenter des comparaisons de tri et d'égalité, ainsi que les types délégués génériques pour les gestionnaires d'événements, les conversions et les prédicats de recherche, sont fournies dans l'espace de noms System.

L’espace System.Numerics de noms fournit des interfaces génériques pour les fonctionnalités mathématiques (disponibles dans .NET 7 et versions ultérieures). Pour plus d’informations, consultez Mathématiques génériques.

La prise en charge des génériques a été ajoutée dans l'espace de noms System.Reflection pour examiner les types génériques et les méthodes génériques, dans System.Reflection.Emit pour émettre des assemblages dynamiques qui contiennent des types et des méthodes génériques, et dans System.CodeDom pour générer des graphes source qui incluent des génériques.

Le Common Language Runtime fournit de nouveaux opcodes et préfixes pour prendre en charge les types génériques dans le langage intermédiaire commun (CIL), y compris Stelem, , LdelemUnbox_Any, Constrained, et Readonly.

Visual C++, C# et Visual Basic prennent toutes en charge la définition et l’utilisation de génériques. Pour plus d’informations sur la prise en charge du langage, consultez Types génériques en Visual Basic, Présentation des génériques et Vue d’ensemble des génériques dans Visual C++.

Types imbriqués et génériques

Un type imbriqué dans un type générique peut dépendre des paramètres de type du type générique englobant. Le Common Language Runtime considère que les types imbriqués sont génériques, même s’ils n’ont pas de paramètres de type générique de leur propre choix. Lorsque vous créez une instance d’un type imbriqué, vous devez spécifier des arguments de type pour tous les types génériques englobants.

Titre Descriptif
Collections génériques dans .NET Décrit les classes de collection générique et d’autres types génériques dans .NET.
Délégués génériques pour manipuler des tableaux et des listes Décrit les délégués génériques pour les conversions, les prédicats de recherche et les actions à entreprendre sur les éléments d’un tableau ou d’une collection.
Mathématiques génériques Décrit comment effectuer des opérations mathématiques de manière générique.
Interfaces génériques Décrit les interfaces génériques qui fournissent des fonctionnalités communes entre les familles de types génériques.
Covariance et Contravariance Décrit la covariance et la contravariance dans les paramètres de type générique.
Types de collection couramment utilisés Fournit des informations récapitulatives sur les caractéristiques et les scénarios d’utilisation des types de collection dans .NET, y compris les types génériques.
Quand utiliser des collections génériques Décrit les règles générales permettant de déterminer quand utiliser des types de collection génériques.
Guide pratique pour définir un type générique avec l’émission de réflexion Explique comment générer des assemblys dynamiques qui incluent des types et des méthodes génériques.
Types génériques en Visual Basic Décrit la fonctionnalité générique pour les utilisateurs de Visual Basic, notamment les rubriques de procédure pour l’utilisation et la définition de types génériques.
Introduction aux génériques Fournit une vue d’ensemble de la définition et de l’utilisation de types génériques pour les utilisateurs C#.
Vue d’ensemble des génériques dans Visual C++ Décrit la fonctionnalité générique pour les utilisateurs C++, y compris les différences entre les génériques et les modèles.

Référence