1. Introduction
Donc, quand on parle d'« événement » (event) dans le contexte de C#, on entend un mécanisme qui permet d'avertir en toute sécurité un ou plusieurs objets qu'il s'est passé quelque chose. Les événements ne sont pas de la magie : sous le capot ce sont des delegates (le mot-clé delegate) avec une protection supplémentaire contre une mauvaise utilisation.
Imaginez vous abonner à une chaîne YouTube. La chaîne (l'objet-propriétaire de l'événement) a un bouton « S'abonner » — n'importe quel spectateur (un autre objet) peut cliquer dessus et apparaître dans la liste des abonnés (les delegates/handlers). Quand l'auteur publie une nouvelle vidéo (lève l'événement), seuls les abonnés reçoivent la notification, et seul le propriétaire de la chaîne décide quand ça arrive. Détail important : les spectateurs peuvent s'abonner ou se désabonner, mais ils ne peuvent pas eux-mêmes envoyer la notification — ce droit appartient uniquement au propriétaire de la chaîne.
Un peu de syntaxe formelle
// Déclaration d'un delegate qui va définir la "forme" du handler d'événement
public delegate void SimpleEventHandler();
// Déclaration d'un événement basé sur le delegate
public event SimpleEventHandler SomethingHappened;
C'est simple : event est le mot-clé pour déclarer un événement, et le type de l'événement est toujours défini par un delegate.
2. Premier exemple simple : l'événement « Bouton cliqué ! »
Étape 1. On définit le delegate pour les handlers
// Handler d'événement : ne retourne rien, ne prend pas de paramètres
public delegate void ButtonClickHandler();
Étape 2. Classe avec un événement
public class Button
{
// Événement : on peut s'abonner et se désabonner, mais on ne peut pas l'invoquer depuis l'extérieur
public event ButtonClickHandler Click;
// Méthode qui "appuie sur le bouton"
public void Press()
{
Console.WriteLine("Bouton pressé !");
// Appel de l'événement : prévenir tous les abonnés
Click?.Invoke();
}
}
Notez : l'événement est déclaré à partir d'un delegate, et dans la méthode Press l'événement est invoqué via ?.Invoke(). Pourquoi ? Parce que l'événement peut être vide (personne ne s'est abonné), alors Click vaut null. L'opérateur d'appel sécurisé garantit que les handlers seront appelés seulement s'il y a des abonnés.
Étape 3. Abonnement à l'événement et exécution
// Exemple d'utilisation
public class Program
{
public static void Main()
{
var button = new Button();
// On ajoute un "écouteur" (on s'abonne à l'événement)
button.Click += OnButtonClicked;
button.Click += () => Console.WriteLine("Encore un handler !");
button.Press(); // On simule l'appui sur le bouton
// On peut se désabonner de l'événement
button.Click -= OnButtonClicked;
button.Press();
}
// Handler d'événement classique
public static void OnButtonClicked()
{
Console.WriteLine("Handler : Le bouton a été pressé !");
}
}
Au premier appui les deux handlers s'exécutent, au deuxième — seulement la lambda.
3. Différences entre événement et delegate
On peut assigner librement une valeur à un delegate et même remplacer complètement la liste des abonnés — quelqu'un pourrait écrire quelque chose comme button.Click = null et toutes les abonnements précédents « disparaîtraient ».
Avec un événement c'est différent — il est lié à l'objet de façon plus stricte. Seul le propriétaire de la classe où l'événement est déclaré peut l'invoquer directement (par ex. Click() depuis l'intérieur de la classe). Tout autre code peut seulement s'abonner via += ou se désabonner via -=, mais pas « remettre tout à zéro ». Ça protège l'encapsulation et empêche de « casser » le système d'abonnements.
4. Signature : types de delegates pour les événements, paramètres
La convention en .NET est la suivante : les handlers d'événements reçoivent deux paramètres — object sender (qui a déclenché l'événement) et les arguments de l'événement (EventArgs). Il est recommandé d'utiliser les delegates EventHandler ou EventHandler<TEventArgs>.
Exemple : delegate avec des paramètres
public delegate void ButtonClickHandler(object sender, EventArgs e);
EventArgs est la classe de base pour transmettre des informations supplémentaires. Si vous avez besoin de plus de données, créez une classe dérivée.
On applique ça à notre bouton
// Classe des arguments d'événement
public class ButtonClickEventArgs : EventArgs
{
public string UserName { get; }
public ButtonClickEventArgs(string userName)
{
UserName = userName;
}
}
public class Button
{
public event EventHandler<ButtonClickEventArgs> Click;
public void Press(string userName)
{
Console.WriteLine("Bouton pressé !");
Click?.Invoke(this, new ButtonClickEventArgs(userName));
}
}
public static void Main()
{
var button = new Button();
button.Click += OnButtonClicked;
button.Press("Vassili");
}
public static void OnButtonClicked(object sender, ButtonClickEventArgs e)
{
Console.WriteLine($"L'utilisateur {e.UserName} a appuyé sur le bouton !");
}
5. Schéma visuel : comment l'événement fonctionne
+-------------+
| Utilisateur |
+-------------+
|
v
+--------------+
| Button.Press|
+--------------+
|
v
+-----------------+ +------------------------+
| Appel de Click?|----->---| Abonné 1 |
+-----------------+ +------------------------+
| | Exécuter le handler |
v +------------------------+
+-----------------+ +------------------------+
| Si abonné |----->---| Abonné 2 |
+-----------------+ +------------------------+
: | Exécuter le handler |
v +------------------------+
— Button.Press invoque l'événement ; les handlers des abonnés s'exécutent dans l'ordre.
6. Petites astuces utiles
Forme recommandée des événements en .NET
Utilisez EventHandler et EventHandler<TEventArgs> pour que votre code soit compatible avec les bibliothèques et outils. Cette approche facilite l'évolution des événements : vous ajoutez de nouvelles propriétés dans vos EventArgs sans casser les abonnés.
Documentation : EventHandler, event.
Événements vs delegates
Si vous avez besoin de lier un composant à une méthode spécifique — un delegate suffit (delegate). Si vous voulez que différentes parties du programme puissent s'abonner et se désabonner à tout moment — utilisez un événement (event).
7. Erreurs typiques et moments gênants lors de la création d'événements
Erreur n°1 : appeler un événement sans vérifier la valeur null. Si l'événement n'a pas d'abonnés, tenter de l'appeler directement conduira à une NullReferenceException. Utilisez l'appel sécurisé :
Click?.Invoke(...);
Erreur n°2 : tenter d'invoquer l'événement depuis l'extérieur de la classe. L'événement ne peut être élevé (invoqué) que depuis la classe où il est déclaré. Depuis une autre classe, le compilateur lèvera une erreur — c'est là pour protéger l'encapsulation.
Erreur n°3 : abonnement multiple au même handler. Si un handler est abonné plusieurs fois, il sera appelé autant de fois. C'est une caractéristique du mécanisme d'abonnement. Faites attention aux duplications de += et au bon usage du désabonnement -=.
GO TO FULL VERSION