Observer Pattern in C#: Definition, Examples, Pros, Cons, and Use Cases
The Observer pattern in C# is a behavioral design pattern that defines a one-to-many dependency where multiple objects automatically receive notifications when another object changes state.
The Observer pattern allows a subject object to maintain a list of dependent observer objects and notify them automatically about state changes. It promotes loose coupling because the subject does not need to know implementation details of its observers. Observers subscribe to the subject and react whenever the subject publishes an update. In C#, the pattern is commonly implemented using events, delegates, or the IObservable and IObserver interfaces. The Observer pattern is heavily used in event-driven systems, UI frameworks, messaging systems, and reactive programming.
Why We Use Observer Pattern in C#?
We use the Observer pattern in C# to:
• Enable event-driven communication
• Support publish-subscribe architectures
• Reduce tight coupling
• Notify multiple objects automatically
• Simplify reactive programming
• Improve extensibility
• Separate state management from reactions
• Support scalable notification systems
When Should We Use Observer Pattern in C#?
The Observer pattern should be used when:
• Multiple objects depend on another object's state
• Automatic notifications are required
• Event-driven behavior is needed
• Loose coupling is important
• Dynamic subscriptions are necessary
• Real-time updates should be propagated
• Reactive systems are being designed
Common Programming Problems Solved by Observer Pattern
• UI event handling
• Notification systems
• Stock market updates
• Real-time dashboards
• Messaging systems
• Event broadcasting
• Chat applications
• Reactive programming pipelines
• Monitoring systems
Structure of Observer Pattern in C#
Typical participants:
• Subject
• Observer Interface
• Concrete Subject
• Concrete Observer
• Client
Basic workflow:
• Observer subscribes to subject
• Subject state changes
• Subject notifies all observers
• Observers react to update
Observer Pattern Examples in C#
Example 1: Stock Price Notification System
Investors should automatically receive stock price updates.
using System;
using System.Collections.Generic;
public interface IObserver
{
void Update(decimal price);
}
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
public class Stock : ISubject
{
private List<IObserver> observers = new List<IObserver>();
private decimal price;
public decimal Price
{
get { return price; }
set
{
price = value;
Notify();
}
}
public void Attach(IObserver observer)
{
observers.Add(observer);
}
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in observers)
{
observer.Update(price);
}
}
}
public class Investor : IObserver
{
private string name;
public Investor(string name)
{
this.name = name;
}
public void Update(decimal price)
{
Console.WriteLine($"{name} notified: New Price = {price}");
}
}
public class Program
{
public static void Main()
{
Stock stock = new Stock();
Investor john = new Investor("John");
Investor alice = new Investor("Alice");
stock.Attach(john);
stock.Attach(alice);
stock.Price = 150;
}
}
Example 2: Weather Station System
Different displays should update automatically when weather data changes.
using System;
using System.Collections.Generic;
public interface IWeatherObserver
{
void Update(int temperature);
}
public class WeatherStation
{
private List<IWeatherObserver> observers = new List<IWeatherObserver>();
public void Subscribe(IWeatherObserver observer)
{
observers.Add(observer);
}
public void SetTemperature(int temperature)
{
foreach (var observer in observers)
{
observer.Update(temperature);
}
}
}
public class PhoneDisplay : IWeatherObserver
{
public void Update(int temperature)
{
Console.WriteLine($"Phone Display: {temperature}°C");
}
}
public class TVDisplay : IWeatherObserver
{
public void Update(int temperature)
{
Console.WriteLine($"TV Display: {temperature}°C");
}
}
public class Program
{
public static void Main()
{
WeatherStation station = new WeatherStation();
station.Subscribe(new PhoneDisplay());
station.Subscribe(new TVDisplay());
station.SetTemperature(28);
}
}
Example 3: Event-Based Button Click System
Multiple actions should execute when a button is clicked.
using System;
public class Button
{
public event Action Clicked;
public void Click()
{
Console.WriteLine("Button clicked");
Clicked?.Invoke();
}
}
public class Logger
{
public void Log()
{
Console.WriteLine("Logging button click");
}
}
public class NotificationService
{
public void SendNotification()
{
Console.WriteLine("Sending notification");
}
}
public class Program
{
public static void Main()
{
Button button = new Button();
Logger logger = new Logger();
NotificationService notification = new NotificationService();
button.Clicked += logger.Log;
button.Clicked += notification.SendNotification;
button.Click();
}
}
Most Known Real-World Use Cases in C#
• Windows Forms and WPF events
• ASP.NET event handling
• Reactive Extensions (Rx.NET)
• Real-time dashboards
• Messaging systems
• Push notification systems
• Monitoring applications
• Event-driven microservices
• Game event systems
Advantages of Observer Pattern in C#
• Supports loose coupling
• Enables dynamic subscriptions
• Simplifies event-driven systems
• Improves extensibility
• Allows multiple observers
• Encourages separation of concerns
• Supports reactive programming
• Easy to add new observers
Disadvantages (Weak Points) of Observer Pattern in C#
• Notification chains may become complex
• Unexpected update order can occur
• Memory leaks may happen from unremoved subscriptions
• Debugging event flows can be difficult
• Too many observers may reduce performance
• Cascading updates can create side effects
• Observers may receive unnecessary updates
Comparison with Similar Patterns
| Pattern | Main Purpose | Communication Style | Coupling Level | Typical Usage | Main Difference from Observer |
|---|---|---|---|---|---|
| Observer | Notify subscribers of changes | Publish-subscribe | Loose | Events, notifications | Focuses on automatic event notification |
| Mediator | Centralize communication | Through mediator | Loose | UI coordination | Coordinates communication instead of broadcasting updates |
| Event Aggregator | Centralize event distribution | Event hub | Very loose | Large applications | Uses centralized event routing |
| Chain of Responsibility | Pass requests sequentially | Handler chain | Loose | Middleware pipelines | Processes requests sequentially instead of notifying all subscribers |
| Command | Encapsulate operations | Invoker to receiver | Loose | Undo systems | Encapsulates executable actions instead of event subscriptions |
Simplified UML-Style Structure
| Component | Responsibility |
|---|---|
| Subject | Maintains observer list and sends notifications |
| Observer Interface | Defines update method |
| ConcreteSubject | Stores state and triggers updates |
| ConcreteObserver | Receives and reacts to notifications |
| Client | Creates and connects subjects and observers |
Creational Patterns in C#
13. Chain of Responsibility
14. Iterator
15. Momento
16. State
17. Template Method
18. Command
19. Mediator
20. Observer
21. Strategy
22. Visitor