1. Introduction
En C# (et dans .NET en général) les événements sont une des manières dont les objets communiquent entre eux. Par exemple, vous avez un bouton dont le clic doit déclencher quelque chose. On pourrait implémenter le traitement d'événements en déclarant une méthode séparée, puis en l'abonnant explicitement à l'événement... Mais souvent on veut simplement "décrire la réaction ici même", sans éparpiller la logique dans différentes parties du fichier.
Les expressions lambda résolvent très bien ce besoin — elles permettent littéralement d'écrire la réaction à l'événement au même endroit où vous vous abonnez. Le minimum de cérémonial — le maximum d'utilité.
Il en va de même pour les callbacks : parfois vous passez à une méthode une logique à exécuter "plus tard", par exemple quand une opération est terminée, et encore une fois — les expressions lambda rendent cela simple et lisible : toute la logique nécessaire est juste là, à l'endroit de l'appel.
Événements et callbacks : l'essentiel et où les lambdas sont utiles
Événement — ce n'est rien d'autre qu'une "enseigne" sur un objet : "ici quelqu'un peut s'abonner et faire quelque chose quand quelque chose arrive".
Callback (appel de retour) — c'est "je te donne une fonction maintenant, et tu l'appelleras quand ce sera nécessaire". Très proche de "laissez votre message — nous vous rappellerons".
Quand vous avez vu les delegates et les events auparavant, vous avez déjà vu une syntaxe du genre :
button.Click += MyButtonClickHandler;
Où MyButtonClickHandler est une méthode spécifique qu'il faut déclarer quelque part. Mais quand la logique est simple, on préfère s'en sortir en deux lignes sans créer une méthode pour une seule action.
C'est là que les lambdas entrent en scène :
button.Click += (sender, args) => { Console.WriteLine("Le bouton a été cliqué !"); };
Laconique, clair, ici même !
2. Lambdas comme gestionnaires d'événements : du simple à l'intéressant
Forme simple : l'ampoule clignote — on écrit quelque chose dans la console
Supposons que nous ayons une classe Button, et que nous voulions réagir à son événement Click.
// Imaginons un bouton comme ceci :
public class Button
{
public event EventHandler? Click;
public void SimulateClick() // Pour l'exemple : on "clique" programmatiquement
{
// Si quelqu'un est abonné — invoquez les handlers
Click?.Invoke(this, EventArgs.Empty);
}
}
Utilisons maintenant ce bouton :
var button = new Button();
// On s'abonne à l'événement du bouton avec une lambda !
button.Click += (sender, args) =>
{
Console.WriteLine("Hourra ! Le bouton a été cliqué !");
};
button.SimulateClick(); // => Hourra ! Le bouton a été cliqué !
Explication :
— Tout ce qui vient après => est le corps de votre "mini-fonction", qui sera appelée quand l'événement surviendra.
— Il n'est pas nécessaire de réfléchir au cycle de vie du handler : il vit tant que votre abonnement existe (+=). Important : si la lambda capture des variables externes et que vous ne vous désabonnez pas de l'événement, cela peut provoquer des fuites de mémoire.
Lambda-handler avec capture de variables
int clickCount = 0;
button.Click += (sender, args) =>
{
clickCount++;
Console.WriteLine($"Le bouton a été cliqué {clickCount} fois !");
};
button.SimulateClick(); // => Le bouton a été cliqué 1 fois !
button.SimulateClick(); // => Le bouton a été cliqué 2 fois !
Ce qui se passe :
La lambda "capture" la variable clickCount de l'extérieur et se souvient de sa valeur entre les appels.
3. Utiliser des expressions lambda pour les callbacks
Le callback est souvent utilisé quand un code doit appeler votre fonction plus tard — par exemple après l'exécution d'un long travail.
Passer une lambda comme callback
Supposons que nous ayons une méthode qui prend un delegate ou une lambda en argument :
void DoWork(Action callback)
{
Console.WriteLine("Le travail commence...");
// Simulation de travail
System.Threading.Thread.Sleep(500); // (Ne faites pas ça dans des applis réelles, c'est bloquant !)
callback(); // Après le travail on appelle le callback
}
DoWork(() => Console.WriteLine("Le travail est terminé !"));
Résultat :
D'abord la console affichera "Le travail commence...", puis "Le travail est terminé!".
On peut passer des lambdas avec des paramètres :
void Calculate(int a, int b, Action<int> onResult)
{
int sum = a + b;
onResult(sum);
}
Calculate(5, 8, result => Console.WriteLine($"Résultat : {result}"));
4. Erreurs typiques en utilisant des lambdas
Ne pas se désabonner des événements
Quand vous vous abonnez à un événement via une lambda, vous n'avez pas de "nom du handler" pour vous désabonner facilement ensuite :
button.Click += (s, e) => Console.WriteLine("...");
button.Click -= ??? // Comment passer votre lambda ici ? Impossible si elle n'est pas sauvegardée.
Si vous devez vraiment vous désabonner — sauvegardez la lambda dans une variable :
EventHandler handler = (s, e) => Console.WriteLine("...");
button.Click += handler;
// ...puis
button.Click -= handler;
Ceci est important pour les objets long-lived : si vous ne vous désabonnez pas, la référence restera et pourra causer une fuite mémoire.
Capture de variables dans une boucle
Classique : abonnement aux événements dans une boucle :
for (int i = 0; i < 3; i++)
button.Click += (s, e) => Console.WriteLine(i); // Toutes les lambdas "se souviennent" du même i
Dans les versions modernes de C# ce comportement est corrigé pour foreach, mais pour for il peut encore y avoir des subtilités — faites toujours attention aux variables capturées !
Si vous voulez que chaque lambda se souvienne de sa propre valeur — créez une copie locale :
for (int i = 0; i < 3; i++)
{
int localI = i;
button.Click += (s, e) => Console.WriteLine(localI);
}
GO TO FULL VERSION