C# Events

An event is a notification mechanism built on top of delegates. When something happens in a program — a button click, a file download completing, a temperature threshold crossed — the object responsible fires an event, and any interested code reacts to it automatically.

The Publisher-Subscriber Model

┌──────────────────────────────────────────────────────────────┐
│              EVENT ARCHITECTURE                              │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  PUBLISHER (raises event)                                    │
│  e.g. Timer, Button, Sensor                                  │
│      │                                                       │
│      │  fires event  ────────────────────────────────┐       │
│      │                                               │       │
│  SUBSCRIBERS (react to event)                        ▼       │
│  • Logger        ← receives notification             │       │
│  • UI updater    ← receives notification             │       │
│  • EmailSender   ← receives notification             │       │
│                                                              │
│  Publisher does not know who subscribes — loose coupling     │
└──────────────────────────────────────────────────────────────┘

Declaring and Raising an Event

Events use the event keyword with a delegate type. The class that owns the event raises it; other classes subscribe to listen.

using System;

class Alarm
{
    // 1. Declare the delegate type
    public delegate void AlarmHandler(string message);

    // 2. Declare the event using that delegate
    public event AlarmHandler AlarmRaised;

    // 3. Raise the event (fire it)
    public void Trigger(string reason)
    {
        Console.WriteLine("Alarm triggered: " + reason);

        if (AlarmRaised != null)       // check if anyone is subscribed
            AlarmRaised(reason);       // fire the event
    }
}

class SecuritySystem
{
    public void OnAlarm(string message)
    {
        Console.WriteLine("[Security] Alert: " + message);
    }
}

class Logger
{
    public void OnAlarm(string message)
    {
        Console.WriteLine("[Log] Recorded: " + message + " at " + DateTime.Now.ToShortTimeString());
    }
}

class Program
{
    static void Main()
    {
        Alarm alarm = new Alarm();

        SecuritySystem security = new SecuritySystem();
        Logger logger = new Logger();

        // Subscribe to the event:
        alarm.AlarmRaised += security.OnAlarm;
        alarm.AlarmRaised += logger.OnAlarm;

        alarm.Trigger("Door opened");
        // Alarm triggered: Door opened
        // [Security] Alert: Door opened
        // [Log] Recorded: Door opened at 10:30 AM
    }
}

EventHandler — The Standard Pattern

C# provides a built-in delegate called EventHandler for events. It is the standard pattern used throughout .NET, including all Windows Forms and WPF events.

// Standard EventHandler signature:
// void MethodName(object sender, EventArgs e)

class DownloadManager
{
    public event EventHandler DownloadCompleted;

    public void StartDownload(string file)
    {
        Console.WriteLine("Downloading: " + file);
        // ... simulate download ...
        OnDownloadCompleted();   // raise event when done
    }

    protected virtual void OnDownloadCompleted()
    {
        DownloadCompleted?.Invoke(this, EventArgs.Empty);
        // ?. = null-conditional — safe if no subscribers
    }
}

class Program
{
    static void Main()
    {
        DownloadManager dm = new DownloadManager();

        dm.DownloadCompleted += (sender, e) =>
        {
            Console.WriteLine("Download finished! Starting extraction...");
        };

        dm.StartDownload("data.zip");
        // Downloading: data.zip
        // Download finished! Starting extraction...
    }
}

Custom EventArgs

To pass data with an event, create a custom class that inherits from EventArgs.

// Custom EventArgs with data:
class TemperatureEventArgs : EventArgs
{
    public double Temperature { get; }
    public string Zone        { get; }

    public TemperatureEventArgs(double temp, string zone)
    {
        Temperature = temp;
        Zone        = zone;
    }
}

class Thermometer
{
    public event EventHandler<TemperatureEventArgs> OverheatDetected;

    public void CheckTemperature(double temp, string zone)
    {
        Console.WriteLine($"Zone {zone}: {temp}°C");

        if (temp > 80)
        {
            OverheatDetected?.Invoke(this, new TemperatureEventArgs(temp, zone));
        }
    }
}

class Program
{
    static void Main()
    {
        Thermometer sensor = new Thermometer();

        sensor.OverheatDetected += (sender, e) =>
        {
            Console.WriteLine($"WARNING! {e.Zone} is at {e.Temperature}°C — shutting down!");
        };

        sensor.CheckTemperature(65, "Server Room A");  // normal
        sensor.CheckTemperature(92, "Server Room B");  // triggers event
    }
}
// Output:
// Zone Server Room A: 65°C
// Zone Server Room B: 92°C
// WARNING! Server Room B is at 92°C — shutting down!

EventArgs Flow Diagram

┌──────────────────────────────────────────────────────────────┐
│  Thermometer (Publisher)                                     │
│      │                                                       │
│  temp > 80 detected                                          │
│      │                                                       │
│  creates TemperatureEventArgs { Temp=92, Zone="Room B" }     │
│      │                                                       │
│  OverheatDetected?.Invoke(this, eventArgs)                   │
│      │                                                       │
│      ▼                                                       │
│  Subscriber receives: (sender, e)                            │
│  e.Temperature = 92                                          │
│  e.Zone = "Server Room B"                                    │
└──────────────────────────────────────────────────────────────┘

Subscribing and Unsubscribing

alarm.AlarmRaised += security.OnAlarm;   // subscribe
alarm.AlarmRaised -= security.OnAlarm;   // unsubscribe

// Always unsubscribe when the subscriber is no longer needed
// to prevent memory leaks (object stays alive as long as subscribed)

event vs Delegate

┌──────────────────────────────┬───────────────────────────────┐
│ Delegate                     │ Event                         │
├──────────────────────────────┼───────────────────────────────┤
│ Anyone can invoke it         │ Only owner class can invoke   │
├──────────────────────────────┼───────────────────────────────┤
│ Anyone can replace all subs  │ Only += and -= allowed outside│
│ with =                       │ the class                     │
├──────────────────────────────┼───────────────────────────────┤
│ Less encapsulation           │ Stronger encapsulation        │
├──────────────────────────────┼───────────────────────────────┤
│ Use for callbacks, lambdas   │ Use for notifications         │
└──────────────────────────────┴───────────────────────────────┘

Quick Summary

┌──────────────────────────────────────────────────────────────┐
│  event EventHandler MyEvent;          → declare event        │
│  MyEvent += HandlerMethod;            → subscribe            │
│  MyEvent -= HandlerMethod;            → unsubscribe          │
│  MyEvent?.Invoke(this, EventArgs.Empty); → raise event       │
│                                                              │
│  Standard pattern:                                           │
│  • EventHandler (no data) or EventHandler<TEventArgs>        │
│  • Custom EventArgs inherits EventArgs                       │
│  • Raise with ?.Invoke() to safely skip if no subscribers    │
└──────────────────────────────────────────────────────────────┘

Events are everywhere in C# — UI button clicks, timer ticks, download completions, sensor alerts. They decouple the code that detects something from the code that reacts to it, making programs more modular, testable, and maintainable. Every professional C# application uses events extensively.

Leave a Comment

Your email address will not be published. Required fields are marked *