Events in C#

Events in C# are a mechanism for one object to notify other objects that something has happened. They are built on delegates and are one of the most important features in event-driven programming, UI development, background processing, and loosely coupled application design.

An event lets one class expose a notification point without giving outside code full control over when that notification is triggered. Subscribers can attach handler methods, and the publisher raises the event when the relevant action occurs.

This idea appears everywhere in .NET. Button click handlers, timers, file-system watchers, notifications, domain events, and many framework callbacks all rely on event-style behavior. To understand events properly, you first need the delegate idea underneath them.


What Is an Event in C#?

An event is a special class member that allows a publisher object to notify subscribers when something happens. The event is based on a delegate type that defines the method signature subscribers must follow.

The key design benefit is control. Outside code can subscribe and unsubscribe, but it cannot usually raise the event directly. Only the class that owns the event controls when the notification is sent.

Why Events Are Important

Events help keep components loosely coupled. The publisher does not need to know every class that might react. It only raises the event. Subscribers decide whether they care and what they want to do when the event occurs.

This design is useful in user interfaces, business workflows, notifications, monitoring systems, and any application area where one action may trigger several different responses.

Basic Event Syntax in C#

public delegate void NotifyDelegate();

public class Publisher
{
    public event NotifyDelegate Notify;
}

Here, Notify is an event based on the NotifyDelegate delegate type. Only methods matching that signature can subscribe to it.

Creating and Raising an Event

A class usually declares an event and then raises it from inside one of its methods when a specific action is completed.

public delegate void OrderPlacedDelegate();

public class OrderService
{
    public event OrderPlacedDelegate OrderPlaced;

    public void PlaceOrder()
    {
        Console.WriteLine("Order placed");
        OrderPlaced?.Invoke();
    }
}

In this example, the event is raised after the order is placed. The null-safe invocation operator ensures the call happens only if there is at least one subscriber.

Subscribing to an Event

Subscribers attach methods to an event by using the += operator.

class Program
{
    static void SendEmail()
    {
        Console.WriteLine("Email notification sent");
    }

    static void Main()
    {
        OrderService service = new OrderService();
        service.OrderPlaced += SendEmail;
        service.PlaceOrder();
    }
}

When PlaceOrder() raises the event, the subscribed method SendEmail() runs automatically.

Unsubscribing from an Event

You can remove a subscriber by using the -= operator.

service.OrderPlaced -= SendEmail;

This is important in long-running applications because forgetting to unsubscribe in some scenarios can keep objects alive longer than expected or cause repeated handler execution.

Events and Multicast Behavior

Because events are built on delegates, they naturally support multicast behavior. That means multiple subscribers can attach to the same event, and all of them are notified when the event is raised.

service.OrderPlaced += SendEmail;
service.OrderPlaced += SendSms;
service.OrderPlaced += UpdateDashboard;

This is one of the main strengths of events. The publisher performs its work once and notifies every interested subscriber.

Standard EventHandler Pattern

In .NET, the most common event pattern uses EventHandler or EventHandler<TEventArgs>. This keeps event APIs consistent across libraries and applications.

public class PaymentService
{
    public event EventHandler PaymentCompleted;

    public void CompletePayment()
    {
        PaymentCompleted?.Invoke(this, EventArgs.Empty);
    }
}

The first argument is usually the sender object, and the second argument contains event data. This pattern makes event APIs predictable and familiar to other developers.

Custom Event Data with EventArgs

If subscribers need extra information, create a custom class derived from EventArgs and use EventHandler<TEventArgs>.

public class OrderEventArgs : EventArgs
{
    public int OrderId { get; set; }
    public decimal Amount { get; set; }
}

public class OrderService
{
    public event EventHandler<OrderEventArgs> OrderPlaced;

    public void PlaceOrder(int id, decimal amount)
    {
        OrderPlaced?.Invoke(this, new OrderEventArgs
        {
            OrderId = id,
            Amount = amount
        });
    }
}

This approach makes events more useful because subscribers receive structured data instead of relying on global state or extra lookups.

Anonymous Handlers and Lambda Subscriptions

Subscribers do not always need named methods. You can subscribe using anonymous methods or lambda expressions when the handler is small and local to the calling context.

service.OrderPlaced += (sender, e) =>
{
    Console.WriteLine("Lambda subscriber executed");
};

This style is common in UI code and short workflow reactions, although named methods are often better when the handler logic is reused or more complex.

Why Events Are Safer Than Raw Delegates

A public delegate field would allow outside code to replace the entire invocation list or even invoke the delegate directly. An event limits outside access so that subscribers can attach and detach, but only the owning class raises the notification. That restriction protects the event flow.

PointEventPublic Delegate Field
Subscribe with +=YesYes
Unsubscribe with -=YesYes
Can outside code invoke it directlyUsually noYes
Can outside code replace invocation listNoYes

That is why events are the normal choice for notifications exposed from classes.

Events in Real Applications

Events are used in desktop applications for clicks and form actions, in background services for status notifications, in domain-driven systems for business events, and in infrastructure code for monitoring and lifecycle callbacks. They allow one part of the system to react without creating tight compile-time dependencies on the publisher.

This makes events a practical architectural tool, not just a language feature. They support cleaner separation between components that produce actions and components that respond to them.

Common Mistakes with Events

  • Forgetting that events are based on delegates and therefore follow delegate signature rules.
  • Invoking an event without checking whether any subscribers are attached.
  • Using a public delegate field instead of an event for notifications.
  • Failing to unsubscribe handlers when object lifetimes matter.
  • Placing too much business logic directly inside event handlers without structure.

These mistakes often lead to brittle code, memory retention issues, or hard-to-follow behavior in larger applications.

Best Practices for Events in C#

  • Use the EventHandler pattern when possible for consistency.
  • Pass structured event data through EventArgs when subscribers need context.
  • Raise events only from inside the owning class.
  • Use null-safe invocation patterns such as ?.Invoke().
  • Unsubscribe handlers when object lifetime management matters.

Events Interview Points

For interviews, remember that events are built on delegates, they support publisher-subscriber communication, subscribers attach using +=, and the publisher raises the event. You should also know the standard EventHandler pattern and why events are safer than exposing a raw public delegate.

Another strong interview point is explaining why events help decouple components and how custom EventArgs classes carry event-specific data.

FAQs on Events in C#

What is an event in C#?

An event is a notification mechanism built on delegates that allows one object to notify subscribed methods when something happens.

What is the difference between an event and a delegate?

A delegate is a type-safe method reference, while an event is a restricted notification member based on a delegate that controls subscription and raising behavior.

Why use EventHandler in C#?

EventHandler and EventHandler<TEventArgs> are standard .NET patterns that make events consistent and easier to understand across codebases.

When should I unsubscribe from an event?

You should unsubscribe when subscriber lifetime management matters, especially in long-running applications, reusable components, and scenarios where handlers should no longer receive notifications.

Publisher and Subscriber Roles

In event-driven design, the class that owns and raises the event is called the publisher. The classes or methods that attach handlers are called subscribers. This language matters because it explains the direction of responsibility. The publisher does not need to know every subscriber personally. It only exposes the event and raises it at the correct time.

Subscribers, on the other hand, decide whether they care about that notification. They attach handlers and react when the event is raised. This separation is one of the reasons events scale well in medium and large applications.

Event Lifecycle in Practice

An event usually follows a simple lifecycle. First, the publisher exposes the event. Second, one or more subscribers attach handlers. Third, the publisher raises the event when something meaningful happens. Fourth, subscribers run their logic. Finally, subscribers may detach when they are no longer interested or when object lifetime requires cleanup.

Thinking in terms of lifecycle helps prevent bugs. For example, if subscribers are attached too early, too late, or never detached in the right context, the application may behave unpredictably or keep unnecessary references alive.

Custom add and remove Accessors

Most events use the default event behavior, but C# also allows custom add and remove accessors. These are useful when you need to control subscription storage, logging, synchronization, or integration with another notification mechanism.

private EventHandler _changed;

public event EventHandler Changed
{
    add
    {
        Console.WriteLine("Subscriber added");
        _changed += value;
    }
    remove
    {
        Console.WriteLine("Subscriber removed");
        _changed -= value;
    }
}

This is a more advanced pattern, but it shows that events are not just simple syntax sugar. They are controlled subscription points.

Events vs Method Calls

A direct method call is best when one class knows exactly what other code should run. An event is better when the publisher should announce something without hardcoding all possible responses. In that sense, an event is more of a broadcast notification than a direct command.

This distinction improves architecture. Commands tell a known target to do work. Events announce that something happened and allow multiple listeners to react independently.

Events in UI and Domain Models

In UI code, events are used for button clicks, selection changes, and input updates. In domain models or business workflows, events can signal actions such as order placed, payment completed, user registered, or report generated. The same language feature supports both simple interface reactions and higher-level business notifications.

That flexibility is why events remain relevant across desktop apps, services, and framework code.


Continue learning C# in order
Follow the topic sequence with the previous and next lesson.