Observer Pattern avec événements C#

Découvrez comment implémenter efficacement le pattern Observer en C# avec les événements. Une approche élégante pour découpler vos composants et créer du code plus maintenable et évolutif.

Olivier Dupuy
22 juillet 2025

98

Vues

0

Commentaires

3

Min de lecture

Le pattern Observer est l'un des patrons de conception les plus utilisés en programmation orientée objet, particulièrement dans le développement d'applications .NET modernes. En C#, ce pattern prend une dimension particulière grâce au système d'événements natif du langage, offrant une implémentation élégante et puissante des mécanismes de notification.

Comprendre le Pattern Observer en C#

Le pattern Observer établit une relation un-à-plusieurs entre objets, permettant de notifier automatiquement tous les objets dépendants (observateurs) lorsqu'un changement d'état survient dans l'objet observé (sujet).

Concepts fondamentaux

  • Publisher (Subject) : L'objet contenant l'état à observer
  • Subscriber (Observer) : Les objets souhaitant être notifiés des changements
  • Event : Le mécanisme de notification en C#
  • Delegate : Le type définissant la signature des méthodes de callback

Implémentation basique avec événements C#


public class PriceChangeEventArgs : EventArgs
{
    public decimal OldPrice { get; set; }
    public decimal NewPrice { get; set; }
}

public class Product { private decimal _price; // Déclaration de l'événement public event EventHandler PriceChanged;

public decimal Price { get => _price; set { if (_price != value) { var oldPrice = _price; _price = value; // Déclencher l'événement OnPriceChanged(oldPrice, value); } } }

protected virtual void OnPriceChanged(decimal oldPrice, decimal newPrice) { PriceChanged?.Invoke(this, new PriceChangeEventArgs { OldPrice = oldPrice, NewPrice = newPrice }); } }

Utilisation avancée avec les événements personnalisés


// Définition d'un delegate personnalisé
public delegate void PriceChangeDelegate(decimal oldPrice, decimal newPrice);

public class ProductAdvanced { private decimal _price; // Utilisation du delegate personnalisé public event PriceChangeDelegate PriceChanged;

public void UpdatePrice(decimal newPrice) { var oldPrice = _price; _price = newPrice; // Pattern de vérification null moderne PriceChanged?.Invoke(oldPrice, newPrice); } }

Bonnes pratiques et recommandations

  • Toujours vérifier si l'événement a des abonnés avant de le déclencher (null check)
  • Utiliser des EventArgs personnalisés pour transporter les données
  • Implémenter une méthode protected virtual pour déclencher l'événement
  • Considérer l'utilisation de weak events pour éviter les fuites mémoire

Gestion des erreurs et performances


public class SafeProduct
{
    private decimal _price;
    public event EventHandler PriceChanged;

protected virtual void OnPriceChanged(PriceChangeEventArgs e) { var handler = PriceChanged; if (handler != null) { try { handler(this, e); } catch (Exception ex) { // Loguer l'erreur mais ne pas la propager Debug.WriteLine($"Error in price change handler: {ex.Message}"); } } } }

Tests unitaires


[Fact]
public void PriceChange_ShouldTriggerEvent()
{
    // Arrange
    var product = new Product();
    var eventWasRaised = false;
    decimal capturedOldPrice = 0;
    decimal capturedNewPrice = 0;

product.PriceChanged += (sender, e) => { eventWasRaised = true; capturedOldPrice = e.OldPrice; capturedNewPrice = e.NewPrice; };

// Act product.Price = 100m;

// Assert Assert.True(eventWasRaised); Assert.Equal(0m, capturedOldPrice); Assert.Equal(100m, capturedNewPrice); }

Cas d'usage réels

Le pattern Observer avec événements est particulièrement utile dans les scénarios suivants :

  • Interfaces utilisateur réactives (MVVM)
  • Systèmes de logging et monitoring
  • Mise à jour en temps réel de données
  • Communication entre microservices

Exemple d'implémentation dans un contexte MVVM


public class ProductViewModel : INotifyPropertyChanged
{
    private string _name;
    private decimal _price;

public event PropertyChangedEventHandler PropertyChanged;

public string Name { get => _name; set { if (_name != value) { _name = value; OnPropertyChanged(nameof(Name)); } } }

public decimal Price { get => _price; set { if (_price != value) { _price = value; OnPropertyChanged(nameof(Price)); OnPropertyChanged(nameof(FormattedPrice)); } } }

public string FormattedPrice => $"€{Price:N2}";

protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }

Considérations de performance

Pour optimiser les performances lors de l'utilisation d'événements :

  • Éviter de déclencher des événements inutilement
  • Utiliser des delegates asynchrones pour les opérations longues
  • Implémenter un mécanisme de throttling si nécessaire
  • Gérer correctement le désabonnement des événements

Conclusion

Le pattern Observer, combiné avec le système d'événements C#, offre une solution puissante et flexible pour gérer les notifications dans les applications .NET. En suivant les bonnes pratiques et en comprenant les implications en termes de performance et de gestion de la mémoire, vous pouvez créer des systèmes robustes et maintenables.

Points clés à retenir :

  • Utilisez les EventArgs personnalisés pour une meilleure encapsulation
  • Gérez correctement les null checks et les exceptions
  • Pensez à la performance et à la gestion mémoire
  • Testez rigoureusement vos implémentations
Partager cet article
42
12

Commentaires (0)

Rejoignez la discussion

Connectez-vous pour partager votre avis et échanger avec la communauté

Première discussion

Soyez le premier à partager votre avis sur cet article !

À propos de l'auteur
Olivier Dupuy

Développeur passionné et créateur de contenu technique. Expert en développement web moderne avec ASP.NET Core, JavaScript, et technologies cloud.

Profil
Articles similaires
API versioning strategies
02 août 2025 0
C# & .NET
Cryptographie post-quantique
02 août 2025 0
C# & .NET
Géolocalisation et cartes interactives
02 août 2025 0
C# & .NET
Navigation rapide
Commentaires (0)