Utilisation de délégués (Guide de programmation C#)

Mise à jour : novembre 2007

Un délégué est un type qui encapsule sans risque une méthode, semblable à un pointeur fonction en C et C++. Toutefois à la différence des pointeurs fonction du langage C, les délégués sont orientés objet, indépendants des types et sécurisés. Le type d'un délégué est défini par le nom du délégué. L'exemple suivant déclare un délégué nommé Del qui peut encapsuler une méthode qui prend une chaîne comme argument et retourne void :

public delegate void Del(string message);

Un objet de délégué est normalement construit en fournissant le nom de la méthode que le délégué encapsulera ou à l'aide d'une méthode anonyme. Une fois qu'un délégué est instancié, un appel de méthode fait au délégué sera passé par le délégué à cette méthode. Les paramètres passés au délégué par l'appelant sont passés à la méthode, et la valeur de retour, le cas échéant, de la méthode est retournée à l'appelant par le délégué. C'est ce que l'on appelle appeler le délégué. Un délégué instancié peut être appelé comme s'il s'agissait de la méthode encapsulée elle-même. Par exemple :

// Create a method for a delegate.
public static void DelegateMethod(string message)
{
    System.Console.WriteLine(message);
}
// Instantiate the delegate.
Del handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

Les types délégués sont dérivés de la classe Delegate dans le .NET Framework. Les types délégués sont sealed (ils ne peuvent pas être dérivés) et il n'est pas possible de dériver des classes personnalisées de Delegate. Étant donné que le délégué instancié est un objet, il peut être passé en paramètre, ou assigné à une propriété. Cela permet à une méthode d'accepter un délégué comme un paramètre, et d'appeler le délégué à un moment ultérieur. C'est ce qu'on appelle le rappel asynchrone. Il s'agit d'une méthode commune pour notifier un appelant à l'issue d'un long processus. Lorsqu'un délégué est utilisé de cette manière, le code qui utilise le délégué n'a pas besoin de connaître l'implémentation de la méthode qui est utilisée. La fonctionnalité est semblable à l'encapsulation que fournissent les interfaces. Pour plus d'informations, consultez Quand utiliser des délégués au lieu d'interfaces.

Une autre utilisation commune des rappels consiste à définir une méthode de comparaison personnalisée et de passer ce délégué à une méthode de tri. Cela permet d'intégrer le code de l'appelant à l'algorithme de tri. La méthode de l'exemple suivant utilise le type Del comme un paramètre :

public void MethodWithCallback(int param1, int param2, Del callback)
{
    callback("The number is: " + (param1 + param2).ToString());
}

Vous pouvez passer ensuite le délégué créé ci-dessus à cette méthode :

MethodWithCallback(1, 2, handler);

et recevoir la sortie suivante dans la console :

The number is: 3

Utilisant le délégué comme une abstraction, MethodWithCallback n'a pas besoin d'appeler la console directement - elle n'a pas besoin d'être conçue en vue d'une console. Ce que MethodWithCallback fait est simplement de préparer une chaîne et de la passer à une autre méthode. Cela est particulièrement puissant puisqu'une méthode déléguée peut utiliser n'importe quel nombre de paramètres.

Lorsqu'un délégué est construit pour encapsuler une méthode d'instance, le délégué fait référence à la fois à l'instance et à la méthode. Hormis la méthode qu'il encapsule, un délégué n'a aucune connaissance du type de l'instance et peut donc faire référence à tout type d'objet tant qu'il existe une méthode sur cet objet qui corresponde à la signature du délégué. Lorsqu'un délégué est construit pour encapsuler une méthode statique, il référence uniquement la méthode. Prenons les déclarations suivantes :

public class MethodClass
{
    public void Method1(string message) { }
    public void Method2(string message) { }
}

Conjointement à la méthode statique DelegateMethod montrée précédemment, nous avons désormais trois méthodes pouvant être encapsulées par une instance de Del.

Un délégué peut appeler plusieurs méthodes lorsqu'elles sont appelées. C'est connu sous le nom de multicasting. Ajouter une méthode supplémentaire à la liste de méthodes du délégué - la liste d'appel - nécessite simplement l'ajout de deux délégués à l'aide d'opérateurs d'addition ou d'assignation d'addition ('+' ou '+ ='). Par exemple :

MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;

//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

À ce stade allMethodsDelegate contient trois méthodes dans sa liste d'appel - Method1, Method2 et DelegateMethod. Les trois délégués d'origine, d1, d2 et d3 restent inchangés. Lorsque allMethodsDelegate est appelé, toutes les trois méthodes sont appelées en ordre. Si le délégué utilise des paramètres de référence, la référence est passée séquentiellement à chacune des trois méthodes, et toute modification apportée par une méthode est visible pour la méthode suivante. Lorsqu'une de ces méthodes lève une exception qui n'est pas interceptée à l'intérieur de la méthode, cette exception est passée à l'appelant du délégué et aucune méthode suivante dans la liste d'appel n'est appelée. Si le délégué a une valeur de retour et/ou des paramètres out, il retourne la valeur de retour et les paramètres de la dernière méthode appelée. Pour supprimer une méthode de la liste d'appel, utilisez l'opérateur de décrémentation ou d'assignation de décrémentation ('-' ou '- ='). Par exemple :

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;

Étant donné que les types délégués sont dérivés de System.Delegate, les méthodes et propriétés définies par cette classe peuvent être appelées sur le délégué. Par exemple, pour connaître le nombre de méthodes figurant dans la liste d'appel d'un délégué, vous pouvez écrire :

int invocationCount = d1.GetInvocationList().GetLength(0);

Les délégués possédant plusieurs méthodes dans leur liste d'appel dérivent de MulticastDelegate, qui est une sous-classe de System.Delegate. Le code ci-dessus fonctionne dans les deux cas, car les deux classes prennent en charge GetInvocationList.

Les délégués multicast sont extrêmement employés dans la gestion des événements. Les objets sources d'événement envoient des notifications aux objets destinataires qui se sont inscrits dans le Registre pour recevoir cet événement. Pour s'inscrire, le destinataire crée une méthode conçue pour gérer l'événement, puis crée un délégué pour cette méthode et passe le délégué à la source de l'événement. La source appelle le délégué lorsque l'événement se produit. Le délégué appelle ensuite la méthode de gestion des événements sur le destinataire, en fournissant les données de l'événement. Le type délégué pour un événement donné est défini par la source de l'événement. Pour plus d'informations, consultez Événements (Guide de programmation C#).

La comparaison de délégués de deux types différents assignés lors de la compilation résulte en une erreur de compilation. Si les instances de délégué sont statiquement du type System.Delegate, la comparaison est alors autorisée, mais retournera la valeur false lors de l'exécution. Par exemple :

delegate void Delegate1();
delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
    // Compile-time error.
    //Console.WriteLine(d == e);

    // OK at compile-time. False if the run-time type of f 
    // is not the same as that of d.
    System.Console.WriteLine(d == f);
}

Voir aussi

Concepts

Guide de programmation C#

Référence

Délégués (Guide de programmation C#)

Covariance et contravariance dans les délégués (Guide de programmation C#)

Événements (Guide de programmation C#)