CodeGym /Courses /C# SELF /Subscribing to an event and creating handlers

Subscribing to an event and creating handlers

C# SELF
Level 52 , Lesson 2
Available

1. Syntax for subscribing to an event

Imagine a task management app: when a task is completed, an event fires, and a handler can, for example, send a notification, update the UI, or write something to a log. C# makes subscribing to events convenient and safe: you explicitly indicate which handler will react to which event.

Subscribing to an event in C# is kind of like adding yourself to the guest list at a party:

publisher.MyEvent += HandlerMethod;

Here publisher is the object that declared the event MyEvent, and HandlerMethod is the method that will be called when that event occurs.

Let's see this in the context of a minimal example. Suppose we have a click counter app:

public class Clicker
{
    public event Action Clicked;

    public void Click()
    {
        // Something was clicked!
        Clicked?.Invoke();
    }
}

Here's our "publisher" event Clicked. Now — let's subscribe a handler:

Clicker clicker = new Clicker();

void OnClicked()
{
    Console.WriteLine("Button was clicked!");
}

clicker.Clicked += OnClicked;

// Somewhere in the code
clicker.Click();
// → "Button was clicked!"

How does it work? We added our OnClicked method to the "click" event, and it will be called every time a click happens.

2. Event handlers: what they are and how to declare them

An event handler is a method that will be called when an event fires. Its signature must match the delegate type of the event. For example, if an event is declared as public event Action Clicked;, then the handler must be a method with no parameters and no return value.

Handler for Action

void OnClicked() 
{
    Console.WriteLine("The event happened (Action)!");
}

Handler for the standard EventHandler

When you use the classic approach with EventHandler, the handler takes two parameters: the sender (object sender) and the event data (EventArgs e):

public class Alarm
{
    public event EventHandler AlarmRaised;

    public void RaiseAlarm()
    {
        AlarmRaised?.Invoke(this, EventArgs.Empty);
    }
}

Alarm alarm = new Alarm();

void AlarmHandler(object sender, EventArgs e)
{
    Console.WriteLine("Alarm triggered!");
}

alarm.AlarmRaised += AlarmHandler;
alarm.RaiseAlarm();

Anonymous methods and lambda expressions

C# lets you use anonymous functions and lambda expressions directly when subscribing:

clicker.Clicked += () => Console.WriteLine("Another click!");

Or a bit more involved if the event has arguments:

alarm.AlarmRaised += (sender, e) =>
{
    Console.WriteLine($"Alarm raised by: {sender}");
};

4. Useful nuances

Subscribing and unsubscribing: important details

Real life is when the party gets too crowded or someone wants to go home. With delegates it's the same: a handler can be added (subscribe) and removed (unsubscribe):

// Subscribe
publisher.MyEvent += MyHandler;

// Unsubscribe (when the handler is no longer needed)
publisher.MyEvent -= MyHandler;

Why does this matter? If you don't unsubscribe, especially in big apps, handlers can remain "hanging" and prevent the garbage collector from freeing objects — this leads to memory leaks.

Multiple handlers

You can subscribe as many handlers as you want to a single event. They'll be called in the order they were added.

clicker.Clicked += () => Console.WriteLine("First handler!");
clicker.Clicked += () => Console.WriteLine("Second handler!");

clicker.Click();
// → First handler!
// → Second handler!

You can even subscribe and unsubscribe handlers "on the fly" — that's flexible and convenient.

Why this really matters: real scenarios

  • UI (Windows Forms, WPF, WinUI, MAUI): button clicks, text changes — all of these are events.
  • FileSystemWatcher: notification about new files appearing in a folder.
  • Asynchronous operations: file download completion, task progress.
  • Plugin system: separate modules subscribe to events from the main app.

The event model lets you build extensible architectures: you can add new modules and subscribe to existing events without changing the core code.

Subscription examples

Event signature Subscription example Handler example
event Action
ev += Handler;
void Handler() { ... }
event Action<int>
ev += (x) => { ... };
void Handler(int x) { ... }
event EventHandler
ev += Handler;
void Handler(object s, EventArgs e) { ... }
event EventHandler<CustomArgs>
ev += Handler;
void Handler(object s, CustomArgs e) { ... }

5. Common mistakes and pitfalls

Error #1: handler signature mismatch.
If an event is declared as event Action<int> and you try to subscribe a parameterless method, the compiler will error. Always check that the method matches the event's required signature.

Error #2: capturing variables in lambdas.
When subscribing via a lambda it can capture variables from the surrounding scope (see the topic "Closures"). If the variable changes after subscription, the handler will see the new value, which can lead to unexpected results.

Error #3: subscribing to an uninitialized event source.
If you subscribe to an event before the object that exposes that event is created and initialized, you risk getting a NullReferenceException. Make sure the object is ready before subscribing.

Error #4: multiple subscriptions of the same handler.
If the same handler is subscribed multiple times to an event, it will be called that many times. This is not a bug but a characteristic of events, and it often becomes an unpleasant surprise.

2
Task
C# SELF, level 52, lesson 2
Locked
Multiple Subscribers to an Event
Multiple Subscribers to an Event
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION