loader
Event-Driven OOP: Designing Reactive Systems with Delegates and Events

In today’s fast-paced tech landscape, real-time applications are no longer a luxury—they’re a necessity. Think about stock market tickers, live chat systems, or even smart home IoT devices. These systems must respond to events as they happen, and event-driven programming makes this possible. At its core, this paradigm relies heavily on delegates and events, foundational concepts in object-oriented programming (OOP). Let’s dive into how they work and how you can harness their power for reactive systems.


What Are Delegates and Events?

Imagine you’re organizing a conference. Instead of constantly checking if a guest has arrived (polling), wouldn’t it be better if the guest informed you as soon as they walked in? This is the essence of event-driven programming: a system where objects notify each other of changes or occurrences, fostering efficiency and responsiveness.

In programming terms:

  • Delegates act like pointers to methods, allowing one method to call another without tightly coupling the two.
  • Events are like public announcements that notify subscribers when something happens. They rely on delegates to trigger the associated methods.

Think of delegates as the telephone and events as the ringing phone—when the phone rings (event), the receiver picks it up (delegate method).


How Delegates and Events Work

Let’s see how this plays out in code using a stock market ticker as an example:

C#
public class Stock  
{  
    public string Symbol { get; set; }  
    public decimal Price { get; set; }  
}  

public class StockTicker  
{  
    public delegate void StockPriceChangedHandler(Stock stock);  
    public event StockPriceChangedHandler StockPriceChanged;  

    public void UpdateStock(Stock stock)  
    {  
        Console.WriteLine($"Updating stock: {stock.Symbol} to {stock.Price:C}");  
        StockPriceChanged?.Invoke(stock);  
    }  
}  

public class StockDisplay  
{  
    public void Subscribe(StockTicker ticker)  
    {  
        ticker.StockPriceChanged += DisplayStock;  
    }  

    private void DisplayStock(Stock stock)  
    {  
        Console.WriteLine($"Stock update received: {stock.Symbol} - {stock.Price:C}");  
    }  
}  

// Usage  
var ticker = new StockTicker();  
var display = new StockDisplay();  
display.Subscribe(ticker);  

ticker.UpdateStock(new Stock { Symbol = "AAPL", Price = 145.67m });  
ticker.UpdateStock(new Stock { Symbol = "GOOG", Price = 2800.45m });

Here, the StockTicker class raises an event whenever a stock’s price is updated, and the StockDisplay class listens for these updates to display the changes.


Real-World Applications

  1. Stock Market Tickers: Platforms like Bloomberg and Yahoo Finance rely on real-time updates to inform traders and investors of stock price changes.
  2. Chat Applications: Modern messaging apps like WhatsApp and Slack use event-driven models to notify users instantly about new messages, seen status, and typing indicators.
  3. IoT System: Smart home devices like Nest thermostats or Ring doorbells send alerts (events) to your phone whenever a specific condition is met, such as detecting motion or a temperature change.

Pro Tips for Event-Driven Programming

1. Prevent Memory Leaks in Event Handlers

Memory leaks are a common issue in event-driven systems when subscribers forget to unsubscribe from events. This happens because the publisher holds a strong reference to the subscriber, preventing garbage collection.

Solution: Always unsubscribe from events when the subscriber is no longer needed.

C#
ticker.StockPriceChanged -= display.DisplayStock;

Additionally, consider using the IDisposable interface for automatic cleanup:

C#
public class StockDisplay : IDisposable  
{  
    private StockTicker _ticker;  

    public void Subscribe(StockTicker ticker)  
    {  
        _ticker = ticker;  
        _ticker.StockPriceChanged += DisplayStock;  
    }  

    private void DisplayStock(Stock stock)  
    {  
        Console.WriteLine($"Stock update received: {stock.Symbol} - {stock.Price:C}");  
    }  

    public void Dispose()  
    {  
        if (_ticker != null)  
        {  
            _ticker.StockPriceChanged -= DisplayStock;  
        }  
    }  
}

2. Leverage the WeakEventManager for Long-Lived Objects

In scenarios where objects have long lifetimes (e.g., IoT devices or large-scale enterprise systems), memory leaks can be especially problematic. The WeakEventManager in .NET helps mitigate this by using weak references, ensuring subscribers can be garbage collected even if they forget to unsubscribe.

C#
WeakEventManager<StockTicker, EventArgs>.AddHandler(ticker, "StockPriceChanged", (sender, args) =>  
{  
    Console.WriteLine("Weak event handled.");  
});

This approach provides a safer and more efficient way to manage events in complex systems.


Wrapping Up

Event-driven programming with delegates and events is a cornerstone of building reactive, scalable, and efficient systems. Whether you’re developing a stock ticker, a chat app, or an IoT solution, mastering these concepts ensures your applications remain responsive and robust.

Take the time to apply these practices, and you’ll be well on your way to designing modern, event-driven systems that can handle real-world challenges effortlessly.