C#  

Events in C# Explained with a Real-World Example

Introduction

In C#, events provide a structured way for one component to notify other components when something important happens. They are a core part of building loosely coupled and extensible systems, and are widely used in UI frameworks, backend services, and enterprise applications.

This article explains how events work in C# using a simple, real-world example. The goal is to help beginners and intermediate developers understand the concepts, structure, and best practices behind events.

Note:
This article is written purely for educational purposes. The example focuses on clarity and learning, not on production-level concerns such as threading, logging, error handling, or performance optimization.

What Is an Event in C#?

An event is a mechanism that allows a class (the publisher) to notify one or more other classes (the subscribers) when a specific action occurs.

Key characteristics of events:

  • Support publisher–subscriber pattern

  • Allow multiple subscribers

  • Promote loose coupling

  • Are built on delegates

The recommended .NET pattern uses EventHandler<TEventArgs>.

Real-World Scenario: Patient Recovery Notification

Scenario Description

  • A hospital system tracks patient recovery.

  • When a patient recovers:

    • A Doctor

    • A Nurse

should be notified automatically.

  • The hospital system should not be tightly coupled with doctors or nurses.

This is a natural fit for events.

Step 1: Creating a Custom EventArgs Class

In .NET, event data should be passed using a class that derives from EventArgs.

public class PatientRecoveredEventArgs : EventArgs
 {
    public string PatientName { get; set; }
 }

Why this is needed:

  • Keeps event data strongly typed

  • Follows .NET conventions

  • Allows future extension without breaking subscribers

Step 2: Creating the Publisher Class

The publisher defines and raises the event.

public class PatientRecoveryPublisher
{
    public event EventHandler<PatientRecoveredEventArgs> PatientRecovered;

    protected virtual void OnPatientRecovered(string patientName)
    {
        PatientRecovered?.Invoke(this, new PatientRecoveredEventArgs
        {
            PatientName = patientName
        });
    }

    public void TriggerRecovery(string patientName)
    {
        OnPatientRecovered(patientName);
    }
}

Explanation:

  • event keyword ensures external classes can only subscribe or unsubscribe

  • EventHandler<T> provides a standard delegate signature

  • OnPatientRecovered follows .NET naming guidelines

  • Null-conditional invocation prevents runtime exceptions

Step 3: Creating Subscriber Classes

Subscribers react to the event by providing methods that match the delegate signature.

Doctor Subscriber

public class Doctor
{
    public void OnPatientRecovered(object sender, PatientRecoveredEventArgs e)
    {
        Console.WriteLine($"Doctor notified: Patient {e.PatientName} has recovered.");
    }
}

Nurse Subscriber

public class Nurse
{
    public void OnPatientRecovered(object sender, PatientRecoveredEventArgs e)
    {
        Console.WriteLine($"Nurse notified: Patient {e.PatientName} has recovered.");
    }
}

Key Point:

  • Subscribers do not know about each other

  • They only react to the event

  • They can be added or removed without changing the publisher

Step 4: Wiring Everything Together

The console application connects the publisher and subscribers.

class Program
 {
    static void Main(string[] args)
    {
        var publisher = new PatientRecoveryPublisher();
        var doctor = new Doctor();
        var nurse = new Nurse();

        publisher.PatientRecovered += doctor.OnPatientRecovered;
        publisher.PatientRecovered += nurse.OnPatientRecovered;

        Console.WriteLine("Enter patient name:");
        string patientName = Console.ReadLine();

        publisher.TriggerRecovery(patientName);
    }
 }

What happens internally:

  1. Subscribers register to the event

  2. Publisher raises the event

  3. All subscribers are notified automatically

Console Output

Input:

Enter patient name:
 Rahul

Output:

Doctor notified: Patient Rahul has recovered.
 Nurse notified: Patient Rahul has recovered.

This confirms that multiple subscribers are notified through a single event

Custom Delegate vs EventHandler<TEventArgs>

1. Using EventHandler<TEventArgs> (Recommended)

Use EventHandler<TEventArgs> when:

  • Following standard .NET conventions

  • Event represents a notification

  • Event data fits into an EventArgs object

  • Writing maintainable and reusable code

This is the preferred approach for most applications.

2. Using a Custom Delegate

Example:

public delegate void PatientRecoveredHandler(string patientName);

Use a custom delegate when:

  • Event signature is very specific

  • You want to avoid EventArgs

  • Working with legacy systems

  • Performance-critical scenarios (rare)

Drawbacks of custom delegates:

  • Less consistent with .NET ecosystem

  • Harder to extend later

  • Not standard for public APIs

Recommendation

Prefer EventHandler<TEventArgs> unless you have a strong technical reason not to.

Why This Example Is Educational, Not Production-Level

This example intentionally avoids:

  • Thread safety concerns

  • Asynchronous event handling

  • Exception isolation between subscribers

  • Logging and monitoring

  • Dependency injection

These are important in real applications, but including them would distract from understanding core event concepts.

When Should You Use Events?

Events are suitable for:

  • Notification systems

  • UI interactions

  • Domain events

  • Decoupled communication between components

Avoid events for:

  • Simple one-to-one method calls

  • Tight performance loops

  • Complex workflows requiring ordering or retries

Conclusion

This article demonstrated how to use events in C# through a simple, real-world example. The hospital patient recovery scenario clearly shows the benefits of the publisher–subscriber pattern and follows .NET best practices using EventHandler<TEventArgs>.

While not production-ready, this example is:

  • Easy to understand

  • Interview-friendly

  • Ideal for learning and teaching

Mastering events is an essential step toward building scalable and maintainable C# applications.

Happy Coding 😊