Introduction
The Observer Pattern is a behavioral design pattern that allows an object (the subject) to notify multiple other objects (observers) about state changes. It is commonly used in event-driven programming, messaging systems, and real-time notifications.
C# provides a built-in way to implement this pattern using the IObservable and IObserver interfaces. Unlike traditional event handlers, IObservable offers better flexibility, structured error handling, and built-in subscription management.
Implementation of the Observer Pattern
The following example is based on the ObserverPattern repository by me.
using System;
using System.Collections.Generic;
namespace ObserverPattern
{
// Observable: Order Processor
public class OrderProcessor : IObservable<string>
{
private List<IObserver<string>> observers = new();
public IDisposable Subscribe(IObserver<string> observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void ProcessOrder(int orderId)
{
Notify($"Order {orderId} is Processing");
Notify($"Order {orderId} is Shipped");
Notify($"Order {orderId} is Delivered");
}
private void Notify(string message)
{
foreach (var observer in observers)
observer.OnNext(message);
}
private class Unsubscriber : IDisposable
{
private List<IObserver<string>> _observers;
private IObserver<string> _observer;
public Unsubscriber(List<IObserver<string>> observers, IObserver<string> observer)
{
_observers = observers;
_observer = observer;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
}
}
}
// Observer: Console Logger
public class ConsoleLogger : IObserver<string>
{
public void OnNext(string value) => Console.WriteLine($"[Console] {value}");
public void OnError(Exception error) => Console.WriteLine("[Console] Error occurred.");
public void OnCompleted() => Console.WriteLine("[Console] No more updates.");
}
// Observer: File Logger
public class FileLogger : IObserver<string>
{
private readonly string filePath = "log.txt";
public void OnNext(string value)
{
using StreamWriter writer = new(filePath, append: true);
writer.WriteLine(value);
}
public void OnError(Exception error)
{
using StreamWriter writer = new(filePath, append: true);
writer.WriteLine("[File] Error occurred.");
}
public void OnCompleted()
{
using StreamWriter writer = new(filePath, append: true);
writer.WriteLine("[File] No more updates.");
}
}
// Main Program
class Program
{
static void Main()
{
var orderProcessor = new OrderProcessor();
var consoleLogger = new ConsoleLogger();
var fileLogger = new FileLogger();
using var consoleSubscription = orderProcessor.Subscribe(consoleLogger);
using var fileSubscription = orderProcessor.Subscribe(fileLogger);
orderProcessor.ProcessOrder(101);
}
}
}
1.OrderProcessor (Observable)
-Implements IObservable<string>
-Notifies observers of order status changes
-Allows observers to subscribe and unsubscribe dynamically
2.Observers (ConsoleLogger & FileLogger)
-Implement IObserver
-ConsoleLogger logs messages to the console
-FileLogger writes updates to a log file
3.Subscription Management
-Uses IDisposable to allow safe unsubscription
-Observers receive updates dynamically
Why Not Use Event Handlers (event and delegate)?

Benefits of Using IObservable and IObserver
✅ Better Resource Management: Unsubscribing is handled via IDisposable, reducing memory leaks.
✅ Built-in Error Handling: The OnError(Exception) method provides structured error handling.
✅ Completion Notification: OnCompleted() informs observers when no more updates will be sent.
✅ Supports Asynchronous & Reactive Programming: Works seamlessly with Reactive Extensions (Rx.NET).
✅ Scales Well for Complex Scenarios: Ideal for event-driven architectures where multiple observers must react to state changes dynamically.
Real-World Use Cases
Event-driven programming (e.g., UI events, logging systems)
Stock market applications (e.g., notifying traders about price
changes)
Messaging and notification systems (e.g., email/SMS updates)
Reactive programming (e.g., Rx.NET, streaming APIs)
IoT (Internet of Things) (e.g., sensor data updates)
Conclusion
The Observer Pattern is an essential design pattern for event-driven applications. While event handlers (event and delegate) are useful for simple event propagation, IObservable and IObserver provide a more structured and flexible approach to managing subscriptions, error handling, and completion notifications.
By leveraging this pattern, developers can build scalable, reactive, and event-driven applications that efficiently manage state changes across multiple subscribers.
References
GitHub Repository:
<a href="https://github.com/stevsharp/ObserverPattern" target="_blank">ObserverPattern</a>
Microsoft Docs:
Reactive Extensions (Rx.NET) Official:
Book Reference :
Design Patterns: Elements of Reusable Object-Oriented Software