DEV Community

mohamed Tayel
mohamed Tayel

Posted on

C# Advanced: Subscribing to Events

Meta Description:

Learn how to handle events in C# using a stock management system example. This article covers the fundamentals of subscribing to events, providing practical examples and assignments at various difficulty levels.


Introduction

Events in C# provide an efficient way for objects to communicate without being tightly coupled. They follow the publish-subscribe pattern, where one object (the publisher) notifies other objects (subscribers) when a specific action occurs. In this article, we’ll walk through a practical example of event handling using a stock management system and provide assignments to help you apply the concept at various difficulty levels.


1. What Are Events in C#?

Events in C# are built on delegates and allow for flexible communication between objects. They are particularly useful in scenarios where one object needs to notify other objects that something important has happened.

In our stock management example, the system will notify subscribers when stock levels fall below a certain threshold, triggering actions like restocking and notifying the supplier.


2. Stock Management System Example

Let’s build a simple stock management system that monitors product inventory. When the stock of an item falls below a certain level (5 units), it triggers an event to notify the WarehouseManager and SupplierService to take action.

Step 1: Define the Event in the Inventory Manager

We will define a StockLow event in the InventoryManager class. This event is raised when the stock of an item drops below 5 units.

using System;

public class StockEventArgs : EventArgs
{
    public string ItemName { get; set; }
    public int CurrentStock { get; set; }
}

public class InventoryManager
{
    public event EventHandler<StockEventArgs> StockLow;

    private int _stockLevel;
    public string ItemName { get; set; }

    public InventoryManager(string itemName, int initialStock)
    {
        ItemName = itemName;
        _stockLevel = initialStock;
    }

    public void ProcessSale(int quantity)
    {
        _stockLevel -= quantity;
        Console.WriteLine($"{quantity} {ItemName}(s) sold. Current stock: {_stockLevel}");

        if (_stockLevel < 5)
        {
            OnStockLow();
        }
    }

    protected virtual void OnStockLow()
    {
        StockLow?.Invoke(this, new StockEventArgs { ItemName = this.ItemName, CurrentStock = this._stockLevel });
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The InventoryManager class tracks the stock of a product.
  • The StockLow event is triggered when the stock falls below the threshold of 5 units.
  • The StockEventArgs class is used to pass the item name and current stock to the event subscribers.

Step 2: Subscribe to the Event

Next, we create a WarehouseManager and a SupplierService, both of which will subscribe to the StockLow event. When the stock level falls below the threshold, they take appropriate actions such as restocking or notifying the supplier.

public class WarehouseManager
{
    public void Restock(object sender, StockEventArgs e)
    {
        Console.WriteLine($"Warehouse notified: Restock {e.ItemName} (Current stock: {e.CurrentStock})");
    }
}

public class SupplierService
{
    public void NotifySupplier(object sender, StockEventArgs e)
    {
        Console.WriteLine($"Supplier notified to replenish stock for {e.ItemName}.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        InventoryManager inventory = new InventoryManager("Laptop", 10);
        WarehouseManager warehouse = new WarehouseManager();
        SupplierService supplier = new SupplierService();

        // Subscribe to the StockLow event
        inventory.StockLow += warehouse.Restock;
        inventory.StockLow += supplier.NotifySupplier;

        // Simulate sales that reduce stock
        inventory.ProcessSale(3);  // Stock = 7, no event triggered
        inventory.ProcessSale(4);  // Stock = 3, event triggered
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The WarehouseManager and SupplierService subscribe to the StockLow event.
  • When the stock drops below 5, both subscribers are notified, and actions are triggered (restocking and supplier notification).

3. How Events Work in This System

In this stock management system, the InventoryManager acts as the publisher. When the stock level falls below a certain threshold, it raises the StockLow event. The WarehouseManager and SupplierService are subscribers that respond to the event by restocking and notifying suppliers.

Publisher: InventoryManager

  • Raises the StockLow event when stock levels drop.

Subscribers: WarehouseManager and SupplierService

  • Handle the event by performing actions like restocking and notifying suppliers.

4. Important Event Patterns in C#

Here are some best practices to keep in mind when working with events in C#:

  1. Use EventHandler: Always use EventHandler or EventHandler<T> to follow C# conventions for event signatures.
  2. Unsubscribe from Events: Ensure that subscribers properly unsubscribe from events when no longer needed to avoid memory leaks.
  3. Sequential Execution: Event handlers are executed in the order they were added unless explicitly handled asynchronously.

Full Code: Stock Management System Using Events in C

using System;

public class StockEventArgs : EventArgs
{
    public string ItemName { get; set; }
    public int CurrentStock { get; set; }
}

public class InventoryManager
{
    public event EventHandler<StockEventArgs> StockLow;

    private int _stockLevel;
    public string ItemName { get; set; }

    public InventoryManager(string itemName, int initialStock)
    {
        ItemName = itemName;
        _stockLevel = initialStock;
    }

    public void ProcessSale(int quantity)
    {
        _stockLevel -= quantity;
        Console.WriteLine($"{quantity} {ItemName}(s) sold. Current stock: {_stockLevel}");

        if (_stockLevel < 5)
        {
            OnStockLow();
        }
    }

    protected virtual void OnStockLow()
    {
        StockLow?.Invoke(this, new StockEventArgs { ItemName = this.ItemName, CurrentStock = this._stockLevel });
    }
}

public class WarehouseManager
{
    public void Restock(object sender, StockEventArgs e)
    {
        Console.WriteLine($"Warehouse notified: Restock {e.ItemName} (Current stock: {e.CurrentStock})");
    }
}

public class SupplierService
{
    public void NotifySupplier(object sender, StockEventArgs e)
    {
        Console.WriteLine($"Supplier notified to replenish stock for {e.ItemName}.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        InventoryManager inventory = new InventoryManager("Laptop", 10);
        WarehouseManager warehouse = new WarehouseManager();
        SupplierService supplier = new SupplierService();

        // Subscribe to the StockLow event
        inventory.StockLow += warehouse.Restock;
        inventory.StockLow += supplier.NotifySupplier;

        // Simulate sales that reduce stock
        inventory.ProcessSale(3);  // Stock = 7, no event triggered
        inventory.ProcessSale(4);  // Stock = 3, event triggered
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Assignments to Practice Events in C#

Here are three assignments to help you apply what you’ve learned:

Easy Level:

Modify the InventoryManager class to raise a new event called StockFull when the stock goes above 50 units after a restock. Subscribe to this event in both the WarehouseManager and SupplierService classes.

Hint: Define and raise the StockFull event similarly to the StockLow event.

Medium Level:

Modify the program to track multiple products in the InventoryManager. Create instances for "Laptops" and "Tablets," each with its own stock level. Ensure both can independently trigger stock alerts.

Hint: Use a list or dictionary to manage different products and their stock levels.

Difficult Level:

Make event handling asynchronous. Modify the OnStockLow method to execute event handlers asynchronously using Task.Run. Simulate delays in the Restock and NotifySupplier methods using Task.Delay() to mimic real-world processing times.

Hint: Use async and await to handle tasks concurrently.


Conclusion

Events are a key mechanism for enabling communication between different parts of your application. They allow for loose coupling between components, making your code easier to maintain and extend. The stock management system example provides a clear illustration of how to use events effectively in C#. By completing the provided assignments, you will gain hands-on experience and a deeper understanding of event handling in real-world scenarios.

Top comments (0)