CodeToClarity Logo
Published on ·5 min read·C#

Understanding Events in C#: From Basics to Real-World Usage

Kishan KumarKishan Kumar

Learn C# events step by step with real-world examples. Understand how events work, why they matter, and how to use them cleanly in real applications.

If you’ve been learning C# for a while, you’ve probably heard the term events thrown around a lot.

Button click events. File upload events. Payment success events.

But what exactly is an event in C#?
And more importantly → why should you care as a developer?

Let’s break it down in a simple, practical way → no theory overload, no framework magic, just real-world understanding.


What Is an Event in C#?

In C#, an event is a way for one part of your application to notify other parts that something important just happened.

That’s it.

An event itself does not contain business logic.
Think of it as a signal, not an action.

When an event is raised:

  • It announces: “Hey, something happened!”
  • All subscribed methods are automatically notified
  • Each subscriber decides what to do in response

This is the foundation of event-driven programming.


A Real-Life Analogy: The Doorbell System 🔔

Let’s use a simple real-world analogy.

Imagine a doorbell:

  • The doorbell button doesn’t know who is inside the house

  • When pressed, it just sends out a signal

  • Whoever is listening reacts:

    • Someone opens the door
    • A security system starts recording
    • A smart assistant sends a notification

Mapping this to C#

Real WorldC# Concept
Doorbell buttonEvent publisher
Bell ringingEvent
People reactingEvent subscribers

The doorbell does not care who responds → it simply announces the event.

That’s exactly how events work in C#.


Why Do We Even Need Events?

Let’s talk about a common beginner mistake.

Without Events (Tightly Coupled Code)

notificationService.SendEmail();
logger.Log();
receiptService.Generate();

This approach causes problems:

  • One class knows too much about others
  • Adding new behavior means modifying existing code
  • Code becomes fragile and hard to maintain

With Events (Loosely Coupled Code)

With events:

  • The publisher doesn’t know who is listening
  • New behavior can be added without touching existing logic
  • The system becomes flexible and extensible

Loose coupling is the real superpower here.


How Events Work Behind the Scenes

Events in C# are built on top of delegates.

Think of it like this:

  • A delegate defines what kind of method can respond
  • An event controls who can raise that delegate

Why Not Use Delegates Directly?

Because events add safety:

  • ✅ Only the declaring class can raise the event
  • ✅ External classes can only subscribe or unsubscribe
  • ❌ External classes cannot invoke the event directly

This prevents accidental misuse and enforces cleaner design.


Basic Structure of an Event in C#

Every event implementation has three core parts:

  1. A delegate (method signature)
  2. An event declaration
  3. Subscriber methods

Let’s build one step by step.


Step-by-Step: Simple Event Example

Step 1: Declare a Delegate

public delegate void NotificationHandler(string message);

This defines the shape of methods that can respond to the event.


Step 2: Declare and Raise the Event

public class OrderService
{
    public event NotificationHandler OrderPlaced;

    public void PlaceOrder(string orderId)
    {
        Console.WriteLine($"Order placed: {orderId}");

        OrderPlaced?.Invoke($"Order {orderId} has been placed");
    }
}

Notice:

  • OrderPlaced is raised only inside OrderService
  • ?.Invoke() safely triggers all subscribers

Step 3: Create Subscriber Methods

public class CodeToClarityNotificationService
{
    public void SendEmail(string message)
    {
        Console.WriteLine("Email sent: " + message);
    }

    public void SendSms(string message)
    {
        Console.WriteLine("SMS sent: " + message);
    }
}

Step 4: Wire Everything Together

class Program
{
    static void Main()
    {
        var orderService = new OrderService();
        var notificationService = new CodeToClarityNotificationService();

        orderService.OrderPlaced += notificationService.SendEmail;
        orderService.OrderPlaced += notificationService.SendSms;

        orderService.PlaceOrder("ORD-101");
    }
}

What Happens Here?

  • Order is placed
  • Event is raised
  • Email and SMS are sent automatically
  • No direct dependency between services

Clean. Flexible. Scalable.


Real-World Scenario: Online Payment System 💳

After a successful payment, multiple things usually happen:

  • Receipt is generated
  • User is notified
  • Transaction is logged
  • Analytics are updated

Perfect use case for events.


Example: Payment Success Event

public delegate void PaymentSuccessHandler(double amount);

public class PaymentGateway
{
    public event PaymentSuccessHandler PaymentSuccess;

    public void ProcessPayment(double amount)
    {
        Console.WriteLine($"Payment processed: {amount}");

        PaymentSuccess?.Invoke(amount);
    }
}

Subscribers

public class ReceiptService
{
    public void GenerateReceipt(double amount)
    {
        Console.WriteLine($"Receipt generated for ₹{amount}");
    }
}

public class LoggerService
{
    public void LogPayment(double amount)
    {
        Console.WriteLine($"Payment logged: ₹{amount}");
    }
}

Wiring It Together

class Program
{
    static void Main()
    {
        var gateway = new PaymentGateway();

        gateway.PaymentSuccess += new ReceiptService().GenerateReceipt;
        gateway.PaymentSuccess += new LoggerService().LogPayment;

        gateway.ProcessPayment(5000);
    }
}

No service knows about the others.
Each reacts independently. That’s event-driven design.


In real-world .NET applications, you’ll often see this pattern instead of custom delegates.

public class FileUploader
{
    public event EventHandler UploadCompleted;

    public void UploadFile()
    {
        Console.WriteLine("File uploaded successfully");

        UploadCompleted?.Invoke(this, EventArgs.Empty);
    }
}

Subscribing

class Program
{
    static void Main()
    {
        var uploader = new FileUploader();

        uploader.UploadCompleted += (sender, e) =>
        {
            Console.WriteLine("Upload completed event received");
        };

        uploader.UploadFile();
    }
}

Why use EventHandler?

  • Follows .NET conventions
  • Consistent across frameworks
  • Easy for other developers to understand

Subscribing and Unsubscribing Events (Important!)

orderService.OrderPlaced += notificationService.SendEmail; //subscribe
orderService.OrderPlaced -= notificationService.SendEmail; //unsubscribe

Unsubscribing is crucial in:

  • Long-running applications
  • Background services
  • UI apps

Failing to unsubscribe can lead to memory leaks.


Events vs Delegates (Quick Comparison)

DelegatesEvents
Method referencesNotifications
Can be invoked externallyCan only be raised internally
FlexibleSafer
Low-levelHigh-level design

Rule of thumb:
Use events when notifying others.
Use delegates for internal logic.


Where Are Events Used in Real Applications?

You’ll see events everywhere:

  • UI frameworks (button clicks, input changes)
  • ASP.NET Core middleware & filters
  • Background workers
  • Messaging systems
  • Logging & monitoring tools

Once you understand events, many framework behaviors suddenly make sense.


Final Thoughts

Events in C# are not just a language feature → they’re a design tool.

They help you:

  • Build loosely coupled systems
  • Scale functionality without breaking code
  • Write cleaner, more maintainable applications

If you truly understand events, you’re no longer just writing C# → you’re thinking like a system designer.

And that’s a huge step forward 🚀