Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
System.Delegate et le
Cet article décrit les classes de .NET qui prennent en charge les délégués et comment celles-ci sont mappées au delegate
mot clé.
Définir des types délégués
Commençons par le mot clé « délégué », car c’est principalement ce que vous allez utiliser lorsque vous travaillez avec des délégués. Le code que le compilateur génère lorsque vous utilisez le mot clé delegate
correspond à des appels de méthodes qui invoquent des membres des classes Delegate et MulticastDelegate.
Vous définissez un type délégué à l’aide d’une syntaxe similaire à la définition d’une signature de méthode. Vous venez d’ajouter le delegate
mot clé à la définition.
Continuons à utiliser la méthode List.Sort() comme exemple. La première étape consiste à créer un type pour le délégué de comparaison :
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
Le compilateur génère une classe dérivée de System.Delegate
celle qui correspond à la signature utilisée (dans ce cas, une méthode qui retourne un entier et a deux arguments). Le type de ce délégué est Comparison
. Le Comparison
type délégué est un type générique. Pour plus d’informations, consultez les classes et méthodes génériques.
Notez que la syntaxe peut apparaître comme si elle déclare une variable, mais qu’elle déclare réellement un type. Vous pouvez définir des types délégués à l’intérieur de classes, directement à l’intérieur d’espaces de noms ou même dans l’espace de noms global.
Remarque
La déclaration de types délégués (ou d’autres types) directement dans l’espace de noms global n’est pas recommandée.
Le compilateur génère également des gestionnaires d’ajout et de suppression pour ce nouveau type afin que les clients de cette classe puissent ajouter et supprimer des méthodes de la liste d’appel d’une instance. Le compilateur applique que la signature de la méthode en cours d’ajout ou de suppression correspond à la signature utilisée lors de la déclaration de la méthode.
Déclarer des instances de délégués
Après avoir défini le délégué, vous pouvez créer une instance de ce type. Comme toutes les variables en C#, vous ne pouvez pas déclarer d’instances de délégué directement dans un espace de noms ou dans l’espace de noms global.
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
Le type de la variable est Comparison<T>
, le type délégué défini précédemment. Le nom de la variable est comparator
.
Cet extrait de code ci-dessus a déclaré une variable membre à l’intérieur d’une classe. Vous pouvez également déclarer des variables déléguées qui sont des variables locales ou des arguments aux méthodes.
Appeler des délégués
Vous appelez ce délégué pour invoquer les méthodes qui se trouvent dans la liste d'appel d'un délégué. À l’intérieur de la Sort()
méthode, le code appelle la méthode de comparaison pour déterminer l’ordre de placer des objets :
int result = comparator(left, right);
Dans la ligne ci-dessus, le code appelle la méthode attachée au délégué. Vous traitez la variable comme un nom de méthode et appelez-la à l’aide de la syntaxe d’appel de méthode normale.
Cette ligne de code repose sur une hypothèse dangereuse : il n'y a aucune garantie qu'une cible a été ajoutée au délégué. Si aucune cible n’a été attachée, la ligne ci-dessus entraînerait une NullReferenceException
. Les idiomes utilisés pour résoudre ce problème sont plus compliqués qu’une simple vérification null et sont abordés plus loin dans cette série.
Affecter, ajouter et supprimer des cibles d’appel
C’est ainsi qu’un type délégué est défini et comment les instances de délégué sont déclarées et appelées.
Les développeurs qui souhaitent utiliser la List.Sort()
méthode doivent définir une méthode dont la signature correspond à la définition de type délégué et l’affecter au délégué utilisé par la méthode de tri. Cette affectation ajoute la méthode à la liste d'invocation de cet objet délégué.
Supposons que vous vouliez trier une liste de chaînes par leur longueur. Votre fonction de comparaison peut être la suivante :
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
La méthode est déclarée en tant que méthode privée. C’est parfait. Vous ne souhaiterez peut-être pas que cette méthode fait partie de votre interface publique. Elle peut toujours être utilisée comme méthode de comparaison lorsqu’elle est attachée à un délégué. Le code appelant aura cette méthode attachée à la liste cible de l’objet délégué et peut y accéder via ce délégué.
Vous créez cette relation en transmettant cette méthode à la List.Sort()
méthode :
phrases.Sort(CompareLength);
Notez que le nom de la méthode est utilisé, sans parenthèses. Utiliser la méthode comme argument indique au compilateur de convertir la référence de la méthode en une référence pouvant être utilisée comme cible d'invocation de délégué, et d'attacher cette méthode en tant que cible d'invocation.
Vous pourriez également avoir été explicite en déclarant une variable de type Comparison<string>
et en effectuant une affectation :
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
Dans les utilisations où la méthode utilisée comme cible déléguée est une petite méthode, il est courant d’utiliser la syntaxe d’expression lambda pour effectuer l’affectation :
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
L’utilisation d’expressions lambda pour les cibles déléguées est abordée plus loin dans une section ultérieure.
L’exemple Sort() attache généralement une méthode cible unique au délégué. Toutefois, les objets délégués prennent en charge les listes d’appel qui ont plusieurs méthodes cibles attachées à un objet délégué.
Classes Delegate et MulticastDelegate
La prise en charge linguistique décrite ci-dessus fournit les fonctionnalités et la prise en charge dont vous aurez généralement besoin pour travailler avec des délégués. Ces fonctionnalités sont basées sur deux classes dans le .NET Core Framework : Delegate et MulticastDelegate.
La classe System.Delegate
et sa sous-classe directe unique, System.MulticastDelegate
, fournissent le support infrastructurel nécessaire pour la création de délégués, l'enregistrement des méthodes en tant que cibles de délégués, et l'invocation de toutes les méthodes enregistrées en tant que telles.
Il est intéressant de noter que les classes System.Delegate
et System.MulticastDelegate
ne sont pas elles-mêmes des types délégués. Ils fournissent la base de tous les types délégués spécifiques. Ce même processus de conception de langage a imposé que vous ne puissiez pas déclarer une classe qui dérive à partir de Delegate
ou MulticastDelegate
. Les règles de langage C# l’interdisent.
Au lieu de cela, le compilateur C# crée des instances d’une classe dérivée MulticastDelegate
lorsque vous utilisez le mot clé de langage C# pour déclarer des types délégués.
Cette conception a ses racines dans la première version de C# et .NET. Un des objectifs de l'équipe de conception était de s'assurer que le langage applique la sécurité de type lors de l'utilisation de délégués. Cela signifiait s’assurer que les délégués étaient appelés avec le type approprié et le nombre d’arguments. Et que tout type de retour a été correctement indiqué au moment de la compilation. Les délégués faisaient partie de la version 1.0 de .NET, sortie avant l'introduction des génériques.
La meilleure façon d’appliquer cette sécurité de type était pour le compilateur de créer les classes de délégué concrètes qui représentaient la signature de méthode utilisée.
Même si vous ne pouvez pas créer directement des classes dérivées, vous allez utiliser les méthodes définies sur ces classes. Passons en revue les méthodes les plus courantes que vous utiliserez lorsque vous travaillez avec des délégués.
Le premier fait le plus important à retenir est que chaque délégué avec lequel vous travaillez est dérivé de MulticastDelegate
. Un délégué multicast signifie qu'il est possible d'invoquer plusieurs cibles de méthode en passant par un délégué. La conception d’origine envisage de faire une distinction entre les délégués où une seule méthode cible peut être attachée et appelée, et les délégués où plusieurs méthodes cibles peuvent être attachées et appelées. Cette distinction s’est avérée moins utile dans la pratique qu’à l’origine. Les deux classes différentes ont déjà été créées et ont été dans le framework depuis sa version publique initiale.
Les méthodes que vous utiliserez le plus avec les délégués sont Invoke()
et BeginInvoke()
/ EndInvoke()
.
Invoke()
appelle toutes les méthodes qui ont été attachées à une instance de délégué particulière. Comme vous l'avez vu ci-dessus, vous appelez généralement des délégués en utilisant la syntaxe d'appel de méthode sur la variable du délégué. Comme vous le verrez plus loin dans cette série, il existe des modèles qui fonctionnent directement avec ces méthodes.
Maintenant que vous avez vu la syntaxe du langage et les classes qui prennent en charge les délégués, examinons comment les délégués fortement typés sont utilisés, créés et appelés.