DEV Community

Cover image for Events in C#: When and How to Implement Them
ByteHide
ByteHide

Posted on • Originally published at bytehide.com

Events in C#: When and How to Implement Them

Welcome! You’ve probably been hearing a lot about C# events, but you’re wondering, How does it all tie together? Just sit back, because in this article, you are going to catch up on all the details about C# events and their application.

It’s like learning to play an instrument; before you start composing, you need to understand how to read the musical notes. Before diving into the advanced stuff, let’s understand the basics. Stick with me, and I promise, you’ll be writing captivating symphonies – I mean, coding in C# in no time!

Introduction to C# Events

Hang on, what really is a C# event? Covert operations? Nope! In the world of C#, an event is simply a way that a class can notify other classes or objects when something of interest happens.

Overview of Events in C#

Events in the context of C#. Imagine you’re at a concert. The band playing is the publisher of events (C# class), and the audience (other classes or objects) is waiting for something noteworthy (an event) to happen. When the band starts playing (an event occurs), the audience reacts (responds to the event). In programming terms, events in C# provide notification when a specific action has occurred in an object.

public class AlarmClock
{
    public event Action AlarmSounded;
    public void Start()
    {
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
        AlarmSounded?.Invoke();
    }
}

public class Person
{
    public void WakeUp() => Console.WriteLine("Waking Up!");
}

public class MainProgram
{
    public static void Main()
    {
        AlarmClock alarmClock = new AlarmClock();
        Person person = new Person();
        alarmClock.AlarmSounded += person.WakeUp;
        alarmClock.Start();
    }
}
Enter fullscreen mode Exit fullscreen mode

In this code snippet, the AlarmClock class publishes the AlarmSounded event, which the Person class listens to. When the AlarmSounded event is fired after 5 seconds, the person’s wake-up method responds.

Role of Event Handlers in C#

In a festival, event organizers handle everything, right? Similarly, in C#, event handlers handle the response to an event.

What is an Event Handler in C#

In C# event handling, an “event handler” is a delegate that gets called when a given event is fired. Like a listener who is waiting for their favorite song to play, the event handler is waiting for the right event to take action.

public class Button
{
    public event EventHandler Clicked;
    public void PressButton()
    {
        Clicked?.Invoke(this, EventArgs.Empty);
    }
}

public class User
{
    public void PerformAction()
    {
        Console.WriteLine("Action Performed!");
    }
}
public class MainProgram
{
    public static void Main()
    {
        Button button = new Button();
        User user = new User();
        button.Clicked += user.PerformAction;
        button.PressButton();
    }
}
Enter fullscreen mode Exit fullscreen mode

In the example above, the User class has a method PerformAction that serves as an event handler. Whenever the Button is pressed (event happens), the user performs a particular action.

Implementing Events in C#

Ah! The practical aspect of any programming language, that’s where the real fun begins, isn’t it? It’s time to swing into action and see how we can implement events in C#. Let’s roll up our sleeves and dive into the coding part!

Step-by-Step Guide to Implementing Events in C#

Implementing events in C# is like taking a step-by-step journey of orchestrating when and how certain actions are responded to in your program. Remember, it’s a journey best taken one step at a time. Let’s break down the process:

  • Define a delegate that matches the signature of the event handler method you want to use.
  • Declare an event using the delegate you defined.
  • Implement a method, within the provider class, that raises the event.
  • Create a method in the subscriber class that will handle the event (conforms to the delegate).
  • Subscribe the event handling method to the event.

Sounds like a lot? Let’s simplify this with a real-world example.

Real-World C# Event Example

How about we consider a traffic light system? The lights changing (events) cause cars to react (event handling) accordingly. Here is a simplified version of how this could be coded:

// step 1: define a delegate
public delegate void TrafficLightChangedHandler(string color);

public class TrafficLight
{
    // step 2: declare an event
    public event TrafficLightChangedHandler TrafficLightChanged;
    public void ChangeLight(string color)
    {
        // step 3: method that raises the event
        Console.WriteLine($"The traffic light is {color}.");
        TrafficLightChanged?.Invoke(color);
    }
}
public class Car
{
    public void ReactToLight(string lightColor)
    {
        // step 4: event handling method
        if (lightColor == "Red")
        {
            Console.WriteLine("Car stops.");
        }
        else if (lightColor == "Green")
        {
            Console.WriteLine("Car starts moving.");
        }
    }
}
public class MainProgram
{
    public static void Main(string[] args)
    {
        TrafficLight light = new TrafficLight();
        Car car = new Car();
        // step 5: subscribe to the event
        light.TrafficLightChanged += car.ReactToLight;
        light.ChangeLight("Green");
        light.ChangeLight("Red");
    }
}
Enter fullscreen mode Exit fullscreen mode

In the code above, when the traffic light color changes, the event TrafficLightChanged is fired and causes the car to react. If the light is green, the car moves; if it’s red, the car stops.

Let’s return to the email sending example. In addition to sending the email, we may also need to acknowledge the recipient that the email is being sent.

public class Recipient
{
    public void OnAcknowledge(string recipient)
    {
        Console.WriteLine($"Email is being sent to {recipient}");
    }
}
public class MainProgram
{
    public static void Main(string[] args)
    {
        EmailAcknowledge emailAck = new EmailAcknowledge();
        Recipient recipient = new Recipient();
        emailAck.AcknowledgeSending += recipient.OnAcknowledge;
        emailAck.Acknowledge("Bob");
        emailAck.Acknowledge("Alice");
    }
}
Enter fullscreen mode Exit fullscreen mode

In the newly introduced code snippet, when the Acknowledge method in the EmailAcknowledge class is called, it raises the event AcknowledgeSending, which the Recipient class is listening to. As soon as this event is raised, the OnAcknowledge method in Recipient class gets called and the name of the recipient is printed.

Working with EventArgs in C#

Whew! You’ve made it far. Next stop is EventArgs town. Ready?

Understanding C# Event Args

In C#, ‘EventArgs’ is a class that provides data related to an event. Think of EventArgs as the courier carrying the message that an event has occurred.

public class SongPlayedEventArgs : EventArgs
{
    public string PlayedSong { get; set; }
}

public class MusicPlayer
{
    public event EventHandler<SongPlayedEventArgs> SongPlayed;
    public void PlaySong(string song)
    {
        SongPlayed?.Invoke(this, new SongPlayedEventArgs() { PlayedSong = song });
    }
}

public class Listener
{
    public void OnSongPlayed(object sender, SongPlayedEventArgs e)
    {
        Console.WriteLine($"'{e.PlayedSong}' has been played.");
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, SongPlayedEventArgs carries the name of the song that’s been played. Listener uses this custom data (PlayedSong) to respond to the SongPlayed event.

Advanced Concepts in C# Event-Handling

Take a deep breath, grab a coffee if needed, because we’re about to delve deeper into the abyss of C# events. Just like building a towering Lego skyscraper, it’s all about understanding the intricacies and techniques to assemble the blocks effectively.

Safe Event Invocation

One of the essential aspects of event handling in C# is to always ensure safe event invocation. Listener classes might unsubscribe to an event at any given point in time. Invoking an event without any subscribed listeners could lead to a null reference exception – and trust me, we don’t like those, do we?

Here’s how to safely invoke events:

public class Publisher
{
    public event EventHandler<EventArgs> OnPublish;
    public void Publish()
    {
        OnPublish?.Invoke(this, EventArgs.Empty);
    }
}
Enter fullscreen mode Exit fullscreen mode

The ?. syntax is C#’s shortcut for null checking before invoking the event handlers, saving us from possible null reference exceptions. It’s as though you’re checking if the coffee machine is indeed present before pressing the brew button.

Passing Data Using Custom EventArgs

In many scenarios, we need to pass some data along with our events. That’s where custom EventArgs come into the picture.

Let’s suppose you’re building an app for a library which fires an event whenever a new book arrives.

public class NewBookEventArgs : EventArgs
{
    public string Title { get; set; }
    public string Author { get; set; }
}

public class Library
{
    public event EventHandler<NewBookEventArgs> OnNewBookArrived;
    public void AddBook(string title, string author)
    {
        OnNewBookArrived?.Invoke(this, new NewBookEventArgs { Title = title, Author = author });
    }
}

public class Member
{
    public void OnNewBookArrived(object sender, NewBookEventArgs e)
    {
        Console.WriteLine($"New book arrived! Title: {e.Title}, Author: {e.Author}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, we created a NewBookEventArgs class that inherits from EventArgs and added two properties: Title and Author. We fire the event by creating a new instance of NewBookEventArgs and passing it along with the event invocation.

This way, event handlers (like OnNewBookArrived from the Member class) can access this data and act accordingly.

Unsubscribing from Events

Here’s the thing: every time a class subscribes to an event, it forms a strong reference with the event source. This reference can stop the garbage collector from reclaiming the memory, leading to potential memory leaks (aka, the silent resource murderers!).

Just as you would turn off the lights before exiting a room or unsubscribe from an annoying newsletter in your inbox, remember to unsubscribe from events when they are no longer necessary.

public class Publisher
{
    public event Action OnPublish;
}

public class Subscriber
{
    public void OnPublishing()
    {
        Console.WriteLine("Something has been published!");
    }
    public void Register(Publisher publisher)
    {
        publisher.OnPublish += OnPublishing;
    }
    public void Unregister(Publisher publisher)
    {
        publisher.OnPublish -= OnPublishing;
    }
}

public static class Program
{
    public static void Main()
    {
        Publisher publisher = new Publisher();
        Subscriber subscriber = new Subscriber();
        subscriber.Register(publisher);  // Subscribing to the event
        publisher.OnPublish?.Invoke();  
        subscriber.Unregister(publisher); // Unsubscribing from the event
        publisher.OnPublish?.Invoke(); 
    }
}
Enter fullscreen mode Exit fullscreen mode

The Register and Unregister methods handle the subscription and unsubscription from the publisher.OnPublish event. Simple enough, right? Yes, it’s like you’re giving your consent to receive those marketing emails. And when you decide you’ve had enough? Simply unsubscribe!

Conclusion

Events and the Future of C# Programming

Wow, quite a journey, right? Have events become your new best friends? They should, as encapsulation of behaviours into events streamlines your code, makes it cleaner, and more efficient. It might seem like a challenge now, but the more you meditate on this knowledge, the easier it will be to grasp its essence.

So, go ahead, experiment with some code. Remember, in programming, just as in life, practice makes perfect! Happy coding, folks!

Top comments (2)

Collapse
 
ant_f_dev profile image
Anthony Fung

A great overview!

It's not immediately obvious, but using events are a great way to decouple separate concerns/modules within an app. They can also help when trying to solve circular module dependencies too, so it's definitely worth learning how they work - and this article gives a good head start in that respect.

Collapse
 
fernandoandrade profile image
Fernando Andrade

amazing article, gratzzz