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 ou des classes de base, ou nécessiter un type générique comme référence, valeur ou type non managé. Elles déclarent des fonctionnalités que l’argument de type doit avoir, et elles doivent être placées après toute classe de base déclarée ou interfaces implémentées.
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 la 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. 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, la possibilité de nullabilité du type de classe de base est appliquée. Si la classe de base est non-nullable (par exemple Base
), l’argument de type doit être non-nullable. Si la classe de base peut accepter la valeur Null (par exemple Base?
), l’argument de type peut être un type de référence pouvant accepter la valeur Null 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 est non-nullable.
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 peut 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, la contrainte class
exige qu’un type soit un type référence non-nullable. Pour autoriser les types référence nullables, utilisez la contrainte class?
, qui autorise les types référence nullables et non-nullables.
La clause where
peut inclure la contrainte notnull
. La contrainte notnull
limite le paramètre de type aux types non-nullables. Le type peut être un type de valeur ou un type de référence non-nullable. La contrainte notnull
est disponible pour le code compilé dans un nullable enable
contexte. Contrairement à d’autres contraintes, si un argument de type enfreint la contrainte notnull
, le compilateur génère un avertissement au lieu d’une erreur. Les avertissements sont générés uniquement dans un contexte nullable enable
.
L’ajout de types référence nullables introduit une ambiguïté potentielle dans la signification de T?
dans les méthodes génériques. Si T
est un struct
, T?
est identique à System.Nullable<T>. Toutefois, si T
est un type référence, T?
signifie que null
est une valeur valide. L’ambiguïté survient parce que les méthodes de substitution ne peuvent pas inclure de contraintes. La nouvelle contrainte default
résout cette ambiguïté. Vous l’ajoutez lorsqu’une classe de base ou une interface déclare deux surcharges d’une méthode, l’une qui spécifie la contrainte struct
et l’autre qui n’a pas la contrainte struct
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 contrainte default
pour spécifier que votre classe dérivée remplace 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 contrainte notnull
peuvent être utilisées dans un contexte inconscient 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 types non managés. 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() indique au compilateur que tout argument de type fourni doit avoir un constructeur accessible sans paramètre. 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
, sauf si elle est autorisée par l’anti-contrainte allows ref struct
. 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.
Cette anti-contrainte déclare que l’argument de type pour T
peut être un type ref struct
. Par exemple :
public class GenericRefStruct<T> where T : allows ref struct
{
// Scoped is allowed because T might be a ref struct
public void M(scoped T parm)
{
}
}
La méthode ou le type générique doit obéir aux règles de sécurité de référence pour toute instance de T
, car il est possible que ce soit un ref struct
. La clause allows ref struct
ne peut pas être combinée avec la contrainte class
ou class?
. L’anti-contrainte allows ref struct
doit suivre toutes les contraintes pour cet argument de type.
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.