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:
eventkeyword ensures external classes can only subscribe or unsubscribeEventHandler<T>provides a standard delegate signatureOnPatientRecoveredfollows .NET naming guidelinesNull-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:
Subscribers register to the event
Publisher raises the event
All subscribers are notified automatically
Console Output
Input:
Enter patient name:
RahulOutput:
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
EventArgsobjectWriting 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
EventArgsWorking 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 😊
Join the conversation! Your thoughts help the community grow.