CodeGym /Courses /C# SELF /Creating events using delegates (

Creating events using delegates ( event and delegate)

C# SELF
Level 52, Lesson 1
Available

1. Introduction

So, when we talk about an "event" (event) in the context of C#, we mean a mechanism that lets you safely notify one or more objects that something happened. Events aren't magic: under the hood they are delegates (the delegate keyword) with some extra protection against misuse.

Think of subscribing to a YouTube channel. A channel (the event owner object) has a "Subscribe" button — any viewer (another object) can click it and get into the subscribers list (delegate handlers). When the creator uploads a new video (raises the event), only subscribers get notified, and only the channel owner decides when that happens. Important detail: viewers can subscribe or unsubscribe, but they can't send the notification themselves — only the channel owner can do that.

A bit of formal syntax


// Declaring a delegate that will define the "shape" of the event handler
public delegate void SimpleEventHandler();

// Declaring an event based on the delegate
public event SimpleEventHandler SomethingHappened;

Simple: event is the keyword to declare an event, and the event type is always given by a delegate.

2. First simple example: "Button clicked!" event

Step 1. Define a delegate for event handlers


// Event handler: returns nothing, takes no parameters
public delegate void ButtonClickHandler();

Step 2. Class with an event


public class Button
{
    // Event: you can subscribe and unsubscribe, but you can't raise it from outside
    public event ButtonClickHandler Click;

    // Method that "presses the button"
    public void Press()
    {
        Console.WriteLine("Button pressed!");
        // Raise the event: notify all subscribers
        Click?.Invoke();
    }
}

Note: the event is declared based on a delegate, and in the Press method the event is invoked via ?.Invoke(). Why? Because the event may be empty (nobody subscribed), so Click is null. The safe-call operator ensures handlers are called only when there are subscribers.

Step 3. Subscribing to the event and running the code


// Usage example
public class Program
{
    public static void Main()
    {
        var button = new Button();

        // Add a "listener" (subscribe to the event)
        button.Click += OnButtonClicked;
        button.Click += () => Console.WriteLine("Another handler!");

        button.Press(); // Simulate pressing the button

        // You can unsubscribe from the event
        button.Click -= OnButtonClicked;
        button.Press();
    }

    // Regular event handler
    public static void OnButtonClicked()
    {
        Console.WriteLine("Handler: The button was pressed!");
    }
}

On the first press both handlers will run, on the second — only the lambda.

3. Difference between an event and a delegate

With a delegate you can freely assign values and even completely replace the subscribers list — someone could write something like button.Click = null and all previous subscriptions would be wiped out.

With an event it's different — it's tied to the object more strictly. Only the owner of the class where the event is declared can invoke it directly (for example, Click() from inside the class). Any other code can only subscribe via += or unsubscribe via -=, but cannot "null out" everything at once. This protects encapsulation and prevents breaking the subscription system.

4. Signature: delegate types for events, parameters

The convention in .NET is that event handlers receive two parameters — object sender (who raised the event) and event arguments (EventArgs). It's recommended to use EventHandler or EventHandler<TEventArgs> delegates.

Example: delegate with parameters


public delegate void ButtonClickHandler(object sender, EventArgs e);

EventArgs is the base class for passing additional information. If you need more data, create your own derived class.

Apply this to our button


// Event arguments class
public class ButtonClickEventArgs : EventArgs
{
    public string UserName { get; }

    public ButtonClickEventArgs(string userName)
    {
        UserName = userName;
    }
}

public class Button
{
    public event EventHandler<ButtonClickEventArgs> Click;

    public void Press(string userName)
    {
        Console.WriteLine("Button pressed!");
        Click?.Invoke(this, new ButtonClickEventArgs(userName));
    }
}

public static void Main()
{
    var button = new Button();
    button.Click += OnButtonClicked;

    button.Press("Vasiliy");
}

public static void OnButtonClicked(object sender, ButtonClickEventArgs e)
{
    Console.WriteLine($"User {e.UserName} pressed the button!");
}

5. Visual diagram: how an event works


+-------------+
| User        |
+-------------+
      |
      v
+--------------+
|  Button.Press|
+--------------+
      |
      v
+-----------------+         +------------------------+
|  Invoke Click?  |----->---|   Subscriber 1         |
+-----------------+         +------------------------+
      |                     |   Execute handler      |
      v                     +------------------------+
+-----------------+         +------------------------+
|   If subscriber |----->---|   Subscriber 2         |
+-----------------+         +------------------------+
      :                     |   Execute handler      |
      v                     +------------------------+

— Button.Press raises the event; subscribers' handlers run in sequence.

6. Useful nuances

Recommended event shape in .NET

Use EventHandler and EventHandler<TEventArgs> so your code stays compatible with libraries and tools. This approach makes evolving events easier: you add new properties to your EventArgs without breaking subscribers.

Documentation: EventHandler, event.

Events vs delegates

If you need to tie one component to one specific method — a delegate (delegate) is enough. If you need different parts of the program to be able to subscribe and unsubscribe at any time — use an event (event).

7. Common mistakes and awkward moments when creating events

Mistake #1: invoking an event without checking for null. If an event has no subscribers, attempting to invoke it directly will lead to a NullReferenceException. Use safe invocation:


Click?.Invoke(...);

Mistake #2: trying to raise an event from outside the declaring class. An event can be raised only inside the class where it's declared. From another class the compiler will complain — this protects encapsulation.

Mistake #3: multiple subscriptions of the same handler. If the same handler is subscribed multiple times, it will be called that many times. That's how the subscription mechanism works. Watch out for duplicate += and make sure to unsubscribe correctly with -=.

2
Task
C# SELF, level 52, lesson 1
Locked
Implementing a simple "Button Clicked" event
Implementing a simple "Button Clicked" event
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION