Programmation C sharp/Delegates et events

Les délégués.
Un délégué ("delegate" en anglais) est l'équivalent .Net d'un pointeur de fonction. Son rôle est d'appeler une ou plusieurs méthodes qui peuvent varier selon le contexte.
Syntaxe.
La déclaration d'un délégué définit la signature d'une méthode, dont le type de retour est précédé du mot clé codice_1.
Exemple :
public delegate int CompareDelegate(object a,object b);
Cette déclaration produit en fait une classe codice_2 dérivant de la classe codice_3. Il est donc possible de la placer en dehors de toute classe ou espace de noms, comme pour une classe normale.
Utilisation.
Ce délégué définit donc un nouveau type qui peut être utilisé en paramètre d'une méthode. L'appel à un délégué utilise la même syntaxe que celle d'une méthode.
Exemple :
public void TrierTableau(object[] objects, CompareDelegate compare)
 int moves;
 // Vérifier que le délégué pointe sur une méthode :
 if (compare==null) return;
 do
 moves = 0;
 for (int i=1 ; i<objects.Length ; i++)
 // Si objects[i-1] > objects[i]
 // appel au délégué pour comparer deux objets
 if ( compare( objects[i-1], objects[i]) > 0 )
 // Échange des deux objets mal ordonnés
 object o=objects[i-1];
 objects[i-1]=objects[i];
 objects[i]=o;
 moves++; // un échange de plus
 while (moves!=0);
Instanciation.
Une variable du type délégué peut être déclarée, comme avec une classe normale :
 CompareDelegate comparateur;
Initialement, cette variable vaut codice_4 car ne référence aucune méthode.
Ajout et retrait de fonction.
Un délégué est associé à une ou plusieurs méthodes qui possèdent toutes la même signature que celui-ci.
L'ajout ou le retrait de fonction se fait par les opérateurs codice_5, codice_6 et codice_7.
Exemple : soit les deux méthodes suivantes :
void AfficherConsole(string message)
void AfficherFenetre(string message)
et le délégué suivant :
delegate void AfficherDelegate(string message);
AfficherDelegate affichage;
affichage = AfficherConsole;
affichage += AfficherFenetre;
affichage("Un message affiché de deux manières différentes en un seul appel");
affichage -= AfficherFenetre; // Ne plus afficher par fenêtre
affichage("Un message sans fenêtre");
affichage = AfficherFenetre; // Fenêtre seulement (affectation par =)
affichage("Un message dans une fenêtre");
Il est également possible d'utiliser la syntaxe suivante équivalente à la précédente :
affichage += new AfficherDelegate( AfficherFenetre );
affichage -= new AfficherDelegate( AfficherFenetre );
Délégué anonyme.
Il est possible de créer dynamiquement la fonction associée à un délégué en utilisant la syntaxe anonyme suivante :
 delegate( "arguments" )
Exemple :
affichage += delegate(string m)
La spécification des arguments est optionnelle, à condition qu'aucun des arguments ne soit utilisé par la fonction. Ce qui ne peut être le cas si un des arguments est de type codice_8, car la fonction doit obligatoirement lui attribuer une valeur.
Exemple :
affichage += delegate
Les délégués du framework .Net.
La plupart des délégués du framework .Net sont utilisés comme callback, c'est à dire appelés quand un événement se produit (timer, opération terminée, exécution d'un thread, ...). La signature de ces délégués comporte en général un paramètre nommé codice_9 de type codice_10. Ce paramètre correspond à la valeur transmise au paramètre codice_9 de la méthode appelée utilisant le délégué.
Ce paramètre étant de type codice_10 permet de transmettre toute sorte de valeurs : objets, valeurs numériques (grâce à l'auto-boxing), tableaux de valeurs, ...
Les événements.
Un événement ("event" en anglais) est déclenché en dehors de l'application, par l'utilisateur (frappe au clavier, clic d'un bouton de souris, ...), par le système (connexion réseau, ...), par une autre application.
Gestion par délégué.
Les délégués sont utilisés pour gérer les événements. Toutefois, cela pose un problème si on utilise un délégué public dans une classe :
public delegate void PageRecueDelegate(string url, string contenu);
public class ConnectionHttp
 public PageRecueDelegate PageRecue;
Le code utilisant cette classe peut être le suivant :
ConnectionHttp getweb = new ConnectionHttp();
getweb.PageRecue = StockerPageRecue;
getweb.PageRecue += AfficherPageRecue;
getweb.PageRecue("",""); // appel bidon
Les trois principaux problèmes sont les suivants :
Solution : codice_15.
Les trois problèmes cités précédemment sont résolus par le mot clé codice_15 :
public delegate void PageRecueDelegate(string url, string contenu);
public class ConnectionHttp
 public event PageRecueDelegate PageRecue;
Ce mot clé protège l'accès au délégué de la manière suivante :
Fonctionnement interne.
La protection est réalisée de la manière suivante :
Il est possible de remplacer les accesseurs par défaut créés par le compilateur. Pour l'exemple précédent, les accesseurs par défaut sont définis ainsi :
public delegate void PageRecueDelegate(string url, string contenu);
public class ConnectionHttp
 private PageRecueDelegate pageRecue;
 public event PageRecueDelegate PageRecue
 [MethodImpl(MethodImplOptions.Synchronized)]
 add // paramètre value : fonction à ajouter
 pageRecue += value;
 [MethodImpl(MethodImplOptions.Synchronized)]
 remove // paramètre value : fonction à retirer
 pageRecue -= value;
Les événements du framework .Net.
Les événements sont principalement utilisés dans le framework .Net pour les interfaces graphiques. Le délégué correspondant ne retourne rien (codice_28) et possède deux paramètres : un objet indiquant la source de l'événement (le contrôle), et un objet du type codice_29 dérivant de la classe codice_30, contenant d'autres informations sur l'événement.

Programmation C sharp/Interfaces graphiques