1. Syntaxe d'abonnement à un événement
Imaginez une application de gestion de tâches : quand une tâche est terminée, un événement est déclenché, et un handler peut, par exemple, envoyer une notification, mettre à jour l'UI ou écrire dans le log. C# rend l'abonnement aux événements pratique et sûr : vous signalez explicitement quel handler réagit à quel événement.
S'abonner à un événement en C# — c'est presque comme s'ajouter à la guest list d'une soirée :
publisher.MyEvent += HandlerMethod;
Ici publisher est l'objet qui a déclaré l'événement MyEvent, et HandlerMethod est la méthode qui sera appelée quand l'événement se produit.
Regardons ça dans le contexte d'un exemple minimal. Supposons qu'on ait une application qui compte les clics :
public class Clicker
{
public event Action Clicked;
public void Click()
{
// Quelque chose a été cliqué !
Clicked?.Invoke();
}
}
Voici notre "publisher" de l'événement Clicked. Maintenant — abonons un handler :
Clicker clicker = new Clicker();
void OnClicked()
{
Console.WriteLine("Le bouton a été cliqué !");
}
clicker.Clicked += OnClicked;
// Quelque part dans le code
clicker.Click();
// → "Le bouton a été cliqué !"
Comment ça marche ? On a ajouté notre méthode OnClicked à l'événement "click", et elle sera appelée à chaque fois qu'il y a un clic.
2. Handlers d'événements : lesquels existent et comment les déclarer
Un handler d'événement est une méthode qui sera appelée quand l'événement est déclenché. Sa signature doit correspondre au type de delegate de l'événement. Par exemple, si l'événement est déclaré comme public event Action Clicked;, alors le handler doit être une méthode sans paramètres et sans valeur de retour.
Handler pour Action
void OnClicked()
{
Console.WriteLine("L'événement s'est produit (Action) !");
}
Handler pour le classique EventHandler
Quand vous utilisez l'approche classique avec EventHandler, le handler prend deux paramètres : l'émetteur (object sender) et les données de l'événement (EventArgs e) :
public class Alarm
{
public event EventHandler AlarmRaised;
public void RaiseAlarm()
{
AlarmRaised?.Invoke(this, EventArgs.Empty);
}
}
Alarm alarm = new Alarm();
void AlarmHandler(object sender, EventArgs e)
{
Console.WriteLine("L'alarme a déclenché !");
}
alarm.AlarmRaised += AlarmHandler;
alarm.RaiseAlarm();
Méthodes anonymes et expressions lambda
C# permet d'utiliser des fonctions anonymes et des expressions lambda directement lors de l'abonnement :
clicker.Clicked += () => Console.WriteLine("Another click!");
Ou un peu plus complexe si l'événement a des arguments :
alarm.AlarmRaised += (sender, e) =>
{
Console.WriteLine($"Alarm raised by: {sender}");
};
4. Nuances utiles
Abonnement et désabonnement : nuances importantes
La vraie vie — c'est quand il y a trop de monde à la soirée, ou quand quelqu'un veut partir. Avec les delegates c'est la même chose : on peut ajouter (s'abonner) et enlever (se désabonner) un handler :
// Abonnement
publisher.MyEvent += MyHandler;
// Désabonnement (quand le handler n'est plus nécessaire)
publisher.MyEvent -= MyHandler;
Pourquoi c'est important ? Si on ne se désabonne pas, surtout dans les grosses applis, des handlers peuvent rester "accrochés" et empêcher le garbage collector de libérer des objets — d'où des fuites mémoire.
Plusieurs handlers
On peut abonner autant de handlers qu'on veut à un même événement. Ils seront appelés séquentiellement dans l'ordre d'ajout.
clicker.Clicked += () => Console.WriteLine("Premier handler !");
clicker.Clicked += () => Console.WriteLine("Deuxième handler !");
clicker.Click();
// → Premier handler !
// → Deuxième handler !
Vous pouvez même vous abonner et vous désabonner "à la volée" — c'est flexible et pratique.
Pourquoi c'est vraiment important : scénarios réels
- UI (Windows Forms, WPF, WinUI, MAUI) : clic sur un bouton, changement de texte dans un champ — ce sont des événements.
- FileSystemWatcher : notification de nouveaux fichiers dans un dossier.
- Opérations asynchrones : fin de téléchargement de fichier, progression de tâches.
- Système de plugins : des modules séparés s'abonnent aux événements de l'application principale.
Le modèle basé sur les événements permet de construire des architectures extensibles : vous pouvez ajouter des modules et vous abonner aux événements existants sans modifier le code de base.
Exemples d'abonnement
| Signature de l'événement | Exemple d'abonnement | Exemple de handler |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
5. Erreurs typiques et pièges
Erreur n°1 : mismatch de la signature du handler.
Si l'événement est déclaré comme event Action<int>, et que vous essayez d'abonner une méthode sans paramètres, le compilateur va lever une erreur. Vérifiez toujours que la méthode correspond à la signature requise par l'événement.
Erreur n°2 : capture de variables dans les lambdas.
Lors d'un abonnement via une expression lambda, elle peut capturer des variables de la scope englobante (voir le sujet «Closures»). Si la variable change après l'abonnement, le handler verra la nouvelle valeur, ce qui peut conduire à des résultats inattendus.
Erreur n°3 : abonnement à un événement non initialisé.
Si vous vous abonnez à un événement avant que l'objet qui contient cet événement soit créé et initialisé, vous risquez une NullReferenceException. Avant de vous abonner, assurez-vous que l'objet est prêt à l'emploi.
Erreur n°4 : abonnement multiple du même handler.
Si le même handler est abonné plusieurs fois à un événement, il sera appelé autant de fois. Ce n'est pas un bug, mais une caractéristique du fonctionnement des événements, et ça surprend souvent.
GO TO FULL VERSION