C# Events Explained: Build Event-Driven Applications

Events allow objects in C# to notify other objects when something happens. They are commonly used for user interactions, application notifications, status changes, and communication between loosely coupled components.

What is an Event in C#?

An event is a mechanism that allows a class, known as the publisher, to notify other classes, known as subscribers, when a specific action occurs.

CSHARP
class Button
{
    public event Action? Clicked;

    public void Click()
    {
        Clicked?.Invoke();
    }
}

Publisher and Subscriber

The publisher defines and raises an event, while subscribers register methods that should execute when the event occurs.

CSHARP
Button button = new Button();

button.Clicked += OnButtonClicked;

button.Click();

static void OnButtonClicked()
{
    Console.WriteLine("Button was clicked");
}

Why Use Events?

  • Create loosely coupled components
  • Notify multiple subscribers
  • Implement event-driven behavior
  • Separate event producers from consumers
  • Build reusable application components
  • React to changes without tightly connecting classes

Declaring an Event

An event can be declared using the event keyword with a compatible delegate type.

CSHARP
class OrderService
{
    public event Action? OrderCompleted;

    public void CompleteOrder()
    {
        Console.WriteLine("Order completed");
        OrderCompleted?.Invoke();
    }
}

Subscribing to an Event

The += operator subscribes a method to an event. Once subscribed, the method is called whenever the publisher raises the event.

CSHARP
OrderService service = new OrderService();

service.OrderCompleted += HandleOrderCompleted;

service.CompleteOrder();

static void HandleOrderCompleted()
{
    Console.WriteLine("Sending confirmation");
}

Unsubscribing from an Event

The -= operator removes a previously subscribed handler. Unsubscribing is important when a subscriber should no longer receive notifications.

CSHARP
service.OrderCompleted += HandleOrderCompleted;

service.OrderCompleted -= HandleOrderCompleted;

EventHandler

EventHandler is the standard .NET delegate pattern for events that do not require custom event data.

CSHARP
class DownloadService
{
    public event EventHandler? DownloadCompleted;

    public void Download()
    {
        Console.WriteLine("Downloading...");
        DownloadCompleted?.Invoke(this, EventArgs.Empty);
    }
}

Handling EventHandler

CSHARP
DownloadService service = new DownloadService();

service.DownloadCompleted += OnDownloadCompleted;

service.Download();

static void OnDownloadCompleted(object? sender, EventArgs e)
{
    Console.WriteLine("Download completed");
}

Custom Event Arguments

When an event needs to provide additional information, a custom EventArgs class can be created.

CSHARP
class OrderCompletedEventArgs : EventArgs
{
    public int OrderId { get; }
    public decimal Total { get; }

    public OrderCompletedEventArgs(int orderId, decimal total)
    {
        OrderId = orderId;
        Total = total;
    }
}

EventHandler

EventHandler is the standard generic event pattern for events that carry custom event data.

CSHARP
class OrderService
{
    public event EventHandler<OrderCompletedEventArgs>? OrderCompleted;

    public void CompleteOrder(int orderId, decimal total)
    {
        var args = new OrderCompletedEventArgs(orderId, total);

        OrderCompleted?.Invoke(this, args);
    }
}

Subscribing to Custom Events

CSHARP
OrderService service = new OrderService();

service.OrderCompleted += OnOrderCompleted;

service.CompleteOrder(1001, 499.99m);

static void OnOrderCompleted(
    object? sender,
    OrderCompletedEventArgs e)
{
    Console.WriteLine($"Order: {e.OrderId}");
    Console.WriteLine($"Total: {e.Total}");
}

Event Flow

ComponentResponsibilityExample
PublisherDefines and raises eventOrderService
EventRepresents notificationOrderCompleted
SubscriberRegisters a handlerEmailService
Event HandlerRuns when event occursOnOrderCompleted

Multiple Subscribers

Multiple objects can subscribe to the same event. When the publisher raises the event, all registered handlers are invoked.

CSHARP
service.OrderCompleted += SendEmail;
service.OrderCompleted += UpdateDashboard;
service.OrderCompleted += WriteLog;

service.CompleteOrder(1001, 250m);

Events vs Delegates

FeatureEventDelegate
PurposePublish notificationsReference callable methods
SubscriptionUses += and -=Can be assigned directly
Typical UseEvent-driven communicationCallbacks and behavior
EncapsulationSubscribers cannot normally raise the eventDelegate variable can invoke its target

Common Event Mistakes

  • Forgetting to unsubscribe long-lived event subscriptions
  • Using events when a simple method call is enough
  • Creating custom delegate types unnecessarily
  • Raising an event without checking for subscribers
  • Putting too much business logic inside event handlers
  • Creating event chains that are difficult to trace

Event Best Practices

  • Follow the standard EventHandler pattern for public .NET-style events
  • Use EventHandler when event data is required
  • Keep event arguments focused and meaningful
  • Use null-safe invocation when an event may have no subscribers
  • Unsubscribe when the subscriber lifetime requires it
  • Keep event handlers small and focused

Real-World Applications

  • Button click notifications
  • Order completion events
  • File processing status
  • Download progress
  • Application state changes
  • Logging and monitoring
  • Notification systems

Practice Exercises

  • Create a simple ButtonClicked event
  • Build an OrderCompleted event
  • Subscribe multiple handlers to one event
  • Create custom EventArgs
  • Implement an EventHandler event
  • Practice subscribing and unsubscribing
  • Build a small notification system using events

Conclusion

Events provide a clean way for C# objects to communicate without creating unnecessary dependencies between components. By understanding publishers, subscribers, EventHandler, and custom event arguments, developers can build flexible and maintainable event-driven applications.

Note: Note: Use events for notifications between components, follow the standard .NET event pattern, and manage subscriptions carefully to keep applications maintainable.