Contrainte des génériques en C #
Lorsque vous travaillez avec des classes ou des méthodes génériques, il peut être utile de contraindre les types pouvant être utilisés avec celles-ci. Nous pouvons appliquer de nombreuses contraintes. Découvrez ce qu'ils sont et comment les utiliser.
Les génériques permettent de concevoir des classes et des méthodes différant la spécification d'un ou de plusieurs types jusqu'à ce que la classe ou la méthode soit déclarée et instanciée par le code client. Lorsque le compilateur rencontre un constructeur pour la classe ou un appel de fonction pour la méthode, il génère un code permettant de gérer le type de données spécifique. Les classes génériques peuvent être obligées de n'être utilisées que sur certains types de données. Cela augmente le nombre d'opérations autorisées et d'appels de méthode à ceux pris en charge par le type contraignant et à tous les types de sa hiérarchie d'héritage. Je vais vous présenter les différentes manières de contraindre des types génériques en C #.
Contraindre par le type de valeur
Vous pouvez contraindre un type générique à un type de valeur en définissant la contrainte de type de la manière suivante:
classe ConstrainByValueType où T: struct {}
Ici, le mot clé struct
est utilisé pour contraindre T
à un type de valeur. L’objet peut ensuite être instancié comme comme le nouveau ConstrainByValueType
et vous pouvez spécifier n’importe quel type de valeur à votre guise. Sachez que vous ne pouvez pas utiliser un type de valeur nullable, cela échouera donc nouveau ConstrainByValueType
.
Contrainte pour autoriser uniquement les types de référence
Vous pouvez également contraindre le type à autoriser uniquement les types de référence. . De la même manière que pour les types de valeur, vous utiliseriez le mot clé class pour contraindre le type à un type de référence.
class ConstrainByReferenceType où T: class {}
Contrainte de type d'interface
Vous pouvez contraindre le type générique par interface, autorisant ainsi uniquement les classes implémentant cette interface ou les classes héritant des classes implémentant l'interface en tant que paramètre type. Le code ci-dessous contraint une classe à une interface.
interface IAnimal {}
classe Snake: IAnimal {}
interface IMammal: IAnimal {}
classe Lion: IMammal {}
classe FuteLion: Lion {}
classe ConstrainByInterface où T: IMammal {}
Le type T
ci-dessus est limité à l'interface IMammal
qui autorise uniquement l'accès aux classes qui implémentent cette interface (ou des classes héritées d'une classe implémentant l'interface). type générique. Même si l’IMammal hérite de l’interface
IAnimal
vous ne pouvez pas utiliser Snake
comme type pour T
. Vous pouvez uniquement utiliser le type Lion
ou FuteLion
.
Contraindre par classe
Nous pouvons restreindre un type générique pour autoriser uniquement les paramètres de type d'une classe spécifique, ou des classes qui héritent à partir de cette classe de base spécifique. Suivant l’exemple de la section précédente, créons une classe générique restreinte à la classe Lion
.
de la classe ConstrainByClass où T: Lion {}
Dans ce scénario, seule la classe Lion
ou une classe héritant de Lion
peut être utilisée pour instancier ce type générique.
Une autre contrainte que nous pouvons appliquer est d'assurer que la classe qui sera utilisée comme paramètre de type a un constructeur public, sans paramètre. Pour ce faire, nous utilisons le mot-clé new ()
comme contrainte pour le type générique.
classe ConstrainByParameterlessCtor où T: new () {}
Avec la contrainte ci-dessus, la classe ConstrainByParameterlessCtor
est restreinte aux classes d'utilisation qui ont un constructeur public sans paramètre. ] System.Enum comme contrainte. Le code ci-dessous montre comment utiliser les énumérations comme contrainte pour les génériques.
{
classe ConstrainByEnum où T: System.Enum
{
public null PrintValues ()
{
var values = Enum.GetValues (typeof (T));
foreach (int item dans les valeurs)
Console.WriteLine (Enum.GetName (typeof (T), item));
}
}
enum Rainbow
{
Rouge,
Orange,
Jaune,
Vert,
Bleu,
Indigo,
Violet
}
static void Main (string [] args)
{
var enumGeneric = new ConstrainByEnum ();
enumGeneric.PrintValues ();
Console.Read ();
}
}
Le type T
comme vous le voyez, est limité à des énumérations. La classe dispose d'une méthode PrintValues
imprime les valeurs de type énumération sur la console. L'exécution du code devrait afficher les informations suivantes:
Red
Orange
Jaune
vert
Bleu
Indigo
Violet
La documentation de Microsoft montre également un exemple d'utilisation de la réflexion, mais cette fonctionnalité étant autorisée, elle n'utilise plus la réflexion et améliore les performances.
À partir du C # 7.3, nous pouvons utiliser la contrainte unmanaged
pour spécifier que le paramètre type doit être un type non géré et System.Delegate
ou System.MulticastDelegate
comme contrainte de classe de base. La documentation fournit un exemple et des détails sur l'utilisation de la contrainte non gérée et de la contrainte de délégué .
Combinaison de contraintes
Vous avez vu comment appliquer une contrainte unique à des types génériques. . Bien que cela soit utile, nous pouvons également utiliser ces contraintes en combinaison.
Programme de classe
{
interface IMammal {}
classe FuteLion: IMammal
{
FuteLion privé () {}
}
classe RainbowLion: IMammal {}
classe ConstrainByCombination où T: IMammal, new () {}
static void Main (string [] args)
{
nouveau ConstrainByCombination (); // valide
nouveau ConstrainByCombination (); // Invalide
Console.Read ();
}
}
Dans l'exemple que vous voyez ci-dessus, nous obligerons T
à utiliser IMammal
et doivent avoir un constructeur public sans paramètre. Nous avons deux classes qui implémentent l'interface IMammal
. La classe FuteLion
implémente cette interface mais possède un constructeur privé sans paramètre, ce qui ne satisfait pas la condition de l’utiliser comme type pour la classe générique. La classe RainbowLion
vérifie cette condition et peut donc être utilisée comme paramètre de type pour la classe générique.
Conclusion
Lorsque vous utilisez des classes ou des méthodes génériques, il est parfois utile de contraindre les types peut être utilisé avec eux. Nous pouvons appliquer un certain nombre de contraintes et cet article les résume et explique comment nous pouvons les utiliser. Nous avons également examiné les nouvelles contraintes autorisées à partir de C # 7.3 et la manière de combiner plusieurs contraintes pour appliquer des règles plus strictes concernant les génériques.
Les commentaires sont désactivés en mode Aperçu.
Source link