where (contrainte de type générique) (Référence C#)

La clause where dans une définition générique spécifie des contraintes sur les types qui sont utilisés comme arguments pour les paramètres de type d’un type générique, d’une méthode, d’un délégué ou d’une fonction locale. Les contraintes peuvent spécifier des interfaces, des classes de base ou exiger qu’un type générique soit une référence, une valeur ou un type non managé. Elles déclarent les fonctionnalités que l’argument de type doit avoir.

Vous pouvez, par exemple, déclarer une classe générique, AGenericClass, de telle sorte que le paramètre de type T implémente l’interface IComparable<T> :

public class AGenericClass<T> where T : IComparable<T> { }

Notes

Pour plus d’informations sur la clause where dans une expression de requête, consultez where, clause.

La clause where peut également inclure une contrainte de classe de base. La contrainte de classe de base indique qu’un type à utiliser comme argument de type pour ce type générique a la classe spécifiée comme classe de base, ou est cette classe de base. Si la contrainte de classe de base est utilisée, elle doit apparaître avant toute autre contrainte sur ce paramètre de type. Certains types ne sont pas autorisés comme contrainte de classe de base : Object, Array et ValueType. Avant C# 7,3, EnumDelegate , et MulticastDelegate étaient également interdits en tant que contraintes de classe de base. L’exemple suivant montre les types qui peuvent maintenant être spécifiés comme classe de base :

public class UsingEnum<T> where T : System.Enum { }

public class UsingDelegate<T> where T : System.Delegate { }

public class Multicaster<T> where T : System.MulticastDelegate { }

Dans un contexte Nullable en C# 8,0 et versions ultérieures, la possibilité de valeur null du type de classe de base est appliquée. Si la classe de base n’accepte pas les valeurs null (par exemple Base ), l’argument de type ne doit pas avoir la valeur null. Si la classe de base accepte la valeur null (par exemple Base? ), l’argument de type peut être un type de référence Nullable ou non Nullable. Le compilateur émet un avertissement si l’argument de type est un type référence Nullable lorsque la classe de base n’accepte pas les valeurs NULL.

La clause where peut spécifier que le type est une class ou un struct. La contrainte struct supprime la nécessité de spécifier une contrainte de classe de base de System.ValueType. Le type System.ValueType ne doit pas être utilisé comme contrainte de classe de base. L’exemple suivant montre les contraintes class et struct :

class MyClass<T, U>
    where T : class
    where U : struct
{ }

Dans un contexte Nullable en C# 8,0 et versions ultérieures, la class contrainte requiert qu’un type soit un type référence qui n’accepte pas les valeurs NULL. Pour autoriser les types référence Nullable, utilisez la contrainte, qui autorise à la class? fois les types référence Nullable et non Nullable.

La where clause peut inclure la notnull contrainte. La notnull contrainte limite le paramètre de type aux types non nullables. Le type peut être un type valeur ou un type référence non Nullable. La contrainte est disponible à partir de C# 8,0 pour le notnull code compilé dans un notnull. Contrairement à d’autres contraintes, si un argument de type viole la notnull contrainte, le compilateur génère un avertissement au lieu d’une erreur. Les avertissements sont générés uniquement dans un nullable enable contexte.

L’ajout de types référence Nullable introduit une ambiguïté potentielle au sens de T? dans les méthodes génériques. Si T est un struct , T? est le même que System.Nullable<T> . Toutefois, si T est un type référence, T? cela signifie qu' null il s’agit d’une valeur valide. L’ambiguïté est due au fait que les méthodes de substitution ne peuvent pas inclure de contraintes. La nouvelle default contrainte résout cette ambiguïté. Vous l’ajouterez quand une classe ou une interface de base déclare deux surcharges d’une méthode, une qui spécifie la struct contrainte et une autre qui n’a struct pas de contrainte ou class appliquée :

public abstract class B
{
    public void M<T>(T? item) where T : struct { }
    public abstract void M<T>(T? item);

}

Vous utilisez la default contrainte pour spécifier que votre classe dérivée substitue la méthode sans la contrainte dans votre classe dérivée ou l’implémentation d’interface explicite. Elle n’est valide que sur les méthodes qui remplacent les méthodes de base ou les implémentations d’interface explicites :

public class D : B
{
    // Without the "default" constraint, the compiler tries to override the first method in B
    public override void M<T>(T? item) where T : default { }
}

Important

Les déclarations génériques qui incluent la notnull contrainte peuvent être utilisées dans un contexte oublie Nullable, mais le compilateur n’applique pas la contrainte.

#nullable enable
    class NotNullContainer<T>
        where T : notnull
    {
    }
#nullable restore

La clause where peut aussi inclure une contrainte unmanaged. La contrainte unmanaged limite le paramètre de type aux types connus sous le nom de unmanaged. La contrainte unmanaged facilite l’écriture de code interop de bas niveau en C#. Cette contrainte permet des routines réutilisables sur tous les types non managés. La contrainte unmanaged ne peut pas être combinée avec la contrainte class ou struct. La contrainte unmanaged exige que le type doit être un struct :

class UnManagedWrapper<T>
    where T : unmanaged
{ }

La clause where peut également inclure une contrainte de constructeur, new(). Cette contrainte permet de créer une instance d’un paramètre de type à l’aide de l’opérateur new. La contrainte New () permet au compilateur de savoir que tout argument de type fourni doit avoir un constructeur sans paramètre accessible. Par exemple :

public class MyGenericClass<T> where T : IComparable<T>, new()
{
    // The following line is not possible without new() constraint:
    T item = new T();
}

La contrainte new() apparaît en dernier dans la clause where. La contrainte new() ne peut pas être combinée avec les contraintes struct ou unmanaged. Tous les types répondant à ces contraintes doivent avoir un constructeur sans paramètre accessible, ce qui rend la contrainte new() redondante.

Avec plusieurs paramètres de type, utilisez une clause where pour chaque paramètre de type, par exemple :

public interface IMyInterface { }

namespace CodeExample
{
    class Dictionary<TKey, TVal>
        where TKey : IComparable<TKey>
        where TVal : IMyInterface
    {
        public void Add(TKey key, TVal val) { }
    }
}

Vous pouvez également joindre des contraintes aux paramètres de type des méthodes génériques, comme montré dans l’exemple suivant :

public void MyMethod<T>(T t) where T : IMyInterface { }

Notez que la syntaxe décrivant les contraintes de paramètre de type sur les délégués est la même que celle des méthodes :

delegate T MyDelegate<T>() where T : new();

Pour plus d’informations sur les délégués génériques, consultez Délégués génériques.

Pour plus d’informations sur la syntaxe et l’utilisation de contraintes, consultez Contraintes sur les paramètres de type.

spécification du langage C#

Pour plus d'informations, voir la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.

Voir aussi