Skip to Content
Course content

47: Events and Event Handlers

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why can't I just call a method on another class instead of using an event?

This is the first thing everyone asks, and it's a fair question. If you have a TemperatureSensor class and you want an Alarm class to go off when things get too hot, why not just give the sensor a reference to the alarm and call alarm.Sound()?

The problem is coupling. If you do that, your sensor is now "married" to your alarm. What happens when you decide you also want to log the temperature to a database, send an email to a technician, and turn on a cooling fan? Suddenly, your sensor class is bloated with references to five different systems. It stops being a "sensor" and starts being a "manager of everything that happens when it's hot."

Events let you flip the script. The sensor just shouts, "Hey, I've hit the threshold!" into the void. It doesn't know—or care—who is listening. This makes your code way more flexible. You can add or remove listeners without ever touching the sensor's code again.

What's actually happening with these delegates and EventHandler types?

You'll see delegate and EventHandler thrown around a lot. Think of a delegate as a contract. It says, "I don't know which method will be called, but I guarantee it will have this specific signature (return type and parameters)."

In the old days, we wrote our own delegates. Now, Microsoft provides EventHandler and EventHandler<TEventArgs> because almost every event in .NET follows the same pattern: the sender of the event and some data about what happened. I always recommend using the generic EventHandler<T> version if you need to pass data.


public class TempChangedEventArgs : EventArgs
{
    public double NewTemperature { get; set; }
}

public class TemperatureSensor
{
    // The 'event' keyword prevents external classes from 
    // clearing the list of subscribers or triggering the event themselves.
    public event EventHandler<TempChangedEventArgs> TemperatureReachedThreshold;

    public void CheckTemperature(double currentTemp)
    {
        if (currentTemp > 100)
        {
            // This is where we "fire" the event
            TemperatureReachedThreshold?.Invoke(this, new TempChangedEventArgs { NewTemperature = currentTemp });
        }
    }
}

Notice that ?.Invoke? That's a lifesaver. It checks if anyone is actually subscribed before trying to call the event, preventing a NullReferenceException if no one is listening.

How do I actually connect a method to the event?

We use the += operator to "subscribe" and -= to "unsubscribe." It looks like math, but you're actually adding a method reference to a list of callbacks.

Here is how you'd set up a listener. The method you're connecting (the "handler") must match the signature of the EventHandler—meaning it needs an object sender and the EventArgs type.


public class AlarmSystem
{
    public void OnTemperatureDanger(object sender, TempChangedEventArgs e)
    {
        Console.WriteLine($"ALARM! Temperature hit {e.NewTemperature} degrees!");
    }
}

// Wiring it up:
var sensor = new TemperatureSensor();
var alarm = new AlarmSystem();

// Subscribe the method to the event
sensor.TemperatureReachedThreshold += alarm.OnTemperatureDanger;

// Now, when this happens, the alarm will sound
sensor.CheckTemperature(105); 

One pro tip: always be mindful of when you unsubscribe. If you subscribe a short-lived object to a long-lived object's event, the long-lived object will hold a reference to the short-lived one, preventing the Garbage Collector from cleaning it up. This is a classic C# memory leak. If you +=, make sure you have a plan for when to -=.




📋 Practical Task

Build a Digital Door Lock Notification System

Your task is to implement an event-driven notification system for a security door. Follow these requirements:

  • Create a DoorLock class that has an event called LockStatusChanged.
  • The event should use a custom LockEventArgs class that carries a bool IsLocked property and a string UserID who performed the action.
  • The DoorLock class should have a method called ToggleLock(string userId) that flips the lock state and triggers the event.
  • Create a SecurityLog class with a method that handles the event by printing a message to the console: "User [UserID] changed lock status to [Locked/Unlocked]".
  • In your main program, instantiate both classes, subscribe the logger to the door lock, and trigger the lock status change a few times to verify it works.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.