CodeGym /Cours /C# SELF /Recommandations pour l'organisation du code

Recommandations pour l'organisation du code

C# SELF
Niveau 54, Leçon 1
Disponible

1. Style de déclaration et de nommage des événements

Les événements ne sont pas de simples delegates. C'est une entité distincte pour la communication entre parties de l'application, et sa déclaration doit être claire.

Utilisez le bon type de delegate

Dans 99% des cas utilisez les delegates standard :

  • EventHandler — pour les événements sans données.
  • EventHandler<TEventArgs> — quand il faut passer des paramètres.

La standardisation facilite la maintenance du code et l'intégration avec les bibliothèques .NET. N'inventez pas votre propre delegate si EventHandler convient.


public event EventHandler SomethingHappened; // Pas de données
public event EventHandler<MyEventArgs> DataReceived; // Données supplémentaires présentes

Si une personnalisation particulière est nécessaire — déclarez votre propre delegate, mais c'est rare.

Nommage des événements

En .NET les événements sont nommés au passé : Completed, Clicked, Changed, Received. Cela souligne le fait accompli.

Exemples :


public event EventHandler DataLoaded;    // Les données ont été chargées
public event EventHandler<MessageEventArgs> MessageReceived; // Message reçu
public event EventHandler Saving;        // Le processus de sauvegarde a commencé

Parfois on utilise la forme Changing pour les événements "avant" le changement, pour donner une chance d'intervenir.

2. Organisation de la classe publisher : méthode virtuelle OnEvent

Ajoutez toujours une méthode protégée virtuelle qui déclenche l'événement : point central d'appel, extensibilité via l'héritage et comportement prévisible.


public class FileLoader
{
    public event EventHandler<FileLoadedEventArgs> FileLoaded;

    protected virtual void OnFileLoaded(FileLoadedEventArgs e)
    {
        FileLoaded?.Invoke(this, e);
    }

    public void Load(string filename)
    {
        // ... logique de chargement de fichier ...
        OnFileLoaded(new FileLoadedEventArgs(filename));
    }
}

public class FileLoadedEventArgs : EventArgs
{
    public string FileName { get; }
    public FileLoadedEventArgs(string fileName) => FileName = fileName;
}

Que seule la méthode OnFileLoaded invoque l'événement — c'est plus simple à maintenir et à tester.

3. Règles d'abonnement et de désabonnement : cycle de vie, IDisposable

Si la durée de vie de l'abonné est plus courte que celle du publisher, désabonnez-vous obligatoirement avant la destruction de l'abonné. Il est pratique d'implémenter IDisposable et de se désabonner dans Dispose().


public class TemporaryListener : IDisposable
{
    private readonly Publisher _publisher;

    public TemporaryListener(Publisher publisher)
    {
        _publisher = publisher;
        _publisher.DataReceived += HandleData;
    }

    private void HandleData(object sender, EventArgs e)
    {
        // Traitement des données
    }

    public void Dispose()
    {
        _publisher.DataReceived -= HandleData;
    }
}

// Utilisation avec using:
using (var listener = new TemporaryListener(myPublisher))
{
    // le listener écoute les événements ici
}
// Après être sorti du using - Dispose est appelé, désabonnement effectué

Si vous oubliez de vous désabonner, le publisher gardera la référence au delegate de l'abonné — fuite de mémoire et "objets zombie".

4. Appel thread-safe des événements

Dans du code multithread, les abonnés peuvent être ajoutés/supprimés en parallèle à l'appel de l'événement. Cela mène à des races et à des NullReferenceException. Utilisez le pattern thread-safe : copiez le delegate dans une variable locale.


protected virtual void OnSomethingHappened()
{
    EventHandler handler = SomethingHappened;
    handler?.Invoke(this, EventArgs.Empty);
}

Avec C# 6+ c'est suffisant :


SomethingHappened?.Invoke(this, EventArgs.Empty);

5. Utiliser EventArgs au lieu de object

Ne passez pas de données via object ou des champs de classe. Utilisez un typage strict via des héritiers de EventArgs.


public class DownloadCompletedEventArgs : EventArgs
{
    public string FileName { get; }
    public long Size { get; }
    public DownloadCompletedEventArgs(string fileName, long size)
    {
        FileName = fileName;
        Size = size;
    }
}

public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;

6. Documenter les événements et les abonnés

Documentez : quand l'événement est déclenché, le sens des champs de EventArgs, si un désabonnement est nécessaire et quand.


/// <summary>
/// L'événement survient après un chargement de données réussi.
/// </summary>
public event EventHandler<DataLoadedEventArgs> DataLoaded;

7. Recommandations d'ensemble pour l'architecture des événements

Séparez les responsabilités

Le publisher ne fait que notifier du fait. L'abonné décide lui-même quand s'abonner et se désabonner.

Évitez le "spamming" d'événements

Ne générez pas le même événement des dizaines de fois par seconde sans besoin — surcharge inutile.

Évitez d'utiliser les événements pour une communication bidirectionnelle

Les événements conviennent pour le schéma "un informe — beaucoup écoutent". Pour une communication bidirectionnelle considérez des interfaces, des callbacks ou d'autres mécanismes.

Ne conservez pas dans la classe des références explicites aux abonnés

Ne retenez pas de références explicites aux abonnés — les événements et delegates s'en chargent automatiquement.

8. Antipatterns classiques

Événements sans typage


public event Action<object> SomethingHappened; // On ne sait pas ce qu'il y a dedans

Mauvais : le typage est cassé, des casts sont nécessaires, la maintenabilité est perdue.

Oubli de désabonnement


public class ShortLivedListener
{
    public ShortLivedListener(Publisher p) =>
        p.DataReceived += DoWork;

    private void DoWork(object sender, EventArgs e) { /* ... */ }
    // Pas de Dispose, pas de désabonnement => objets zombie !
}

Violation du SRP

La classe est à la fois publisher, abonné et handler — mélange de responsabilités. Séparez les responsabilités.

9. Application pratique lors d'entretiens et dans les projets

Dans beaucoup de projets basés sur publish-subscribe, une bonne organisation des événements est la clé de la scalabilité et de la maintenabilité. Aux entretiens on demande souvent :

  • implémenter un système d'événements avec le bon typage,
  • montrer la gestion du cycle de vie des abonnés,
  • expliquer l'appel thread-safe des événements.

Un code événementiel propre, documenté et correctement organisé vous fera ressortir parmi les candidats.

2
Mission
C# SELF, niveau 54, leçon 1
Bloqué
Organisation d'événements avec plusieurs abonnés
Organisation d'événements avec plusieurs abonnés
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION