C#
programming
event keyword
.NET
software development

Why do we need the event keyword while defining events?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In C#, an event is built on top of a delegate, but it is not just a delegate with a different name. The event keyword exists to protect the publisher's control over subscription and invocation. Without it, outside code could overwrite the handler list or raise the delegate directly, which breaks encapsulation and makes event-driven code much easier to misuse.

A Public Delegate Is Too Permissive

Suppose you expose a delegate field directly:

csharp
1using System;
2
3public class BadPublisher
4{
5    public Action? SomethingHappened;
6}

Any external code can now do all of the following:

  • subscribe with +=
  • unsubscribe with -=
  • replace the entire invocation list with =
  • invoke the delegate directly
  • clear all handlers by assigning null

That means the publisher no longer controls when the notification happens or who remains subscribed.

Example misuse:

csharp
var publisher = new BadPublisher();
publisher.SomethingHappened = () => Console.WriteLine("Injected");
publisher.SomethingHappened();

This compiles. That is exactly the problem.

What event Changes

Now compare it to a real event:

csharp
1using System;
2
3public class GoodPublisher
4{
5    public event Action? SomethingHappened;
6
7    public void Raise()
8    {
9        SomethingHappened?.Invoke();
10    }
11}

External code can still subscribe and unsubscribe:

csharp
var publisher = new GoodPublisher();
publisher.SomethingHappened += () => Console.WriteLine("Handled");

But external code cannot do this anymore:

  • 'publisher.SomethingHappened = ...'
  • 'publisher.SomethingHappened()'
  • 'publisher.SomethingHappened = null'

Only the containing type can invoke the event or replace the underlying delegate. That is the core value of the event keyword.

The Language-Level Contract

The event keyword tells the compiler to expose only add and remove semantics to outside callers. In effect, it says:

  • subscribers may attach handlers
  • subscribers may detach handlers
  • subscribers may not control publication

That language-level restriction is important because event handling is about ownership. The publisher owns the notification. Subscribers only react to it.

Without that rule, any consumer could fire your event at arbitrary times and put the object into an invalid state.

Encapsulation and Invariants

Imagine a button class, file watcher, or stock ticker. Those types often have internal rules about when an event should fire.

For example, a button click event should be raised only when the button logic determines a click occurred. If a consumer could invoke the event directly, the UI state and the notification stream could diverge.

That is why idiomatic event publishers wrap invocation in a method such as OnSomethingHappened or a domain-specific method like OnPriceChanged.

csharp
1using System;
2
3public class TemperatureSensor
4{
5    public event EventHandler<int>? TemperatureChanged;
6
7    private int _temperature;
8
9    public void Update(int newTemperature)
10    {
11        if (newTemperature == _temperature)
12            return;
13
14        _temperature = newTemperature;
15        TemperatureChanged?.Invoke(this, newTemperature);
16    }
17}

Here the event fires only when the sensor actually changes state.

Custom Add and Remove Logic

Another reason event exists is that C# lets you define custom add and remove accessors. That means an event can do more than store handlers in a backing field. It can synchronize access, log subscriptions, or forward handlers to another source.

csharp
1using System;
2
3public class Relay
4{
5    private Action? _handlers;
6
7    public event Action Triggered
8    {
9        add { _handlers += value; }
10        remove { _handlers -= value; }
11    }
12
13    public void Fire() => _handlers?.Invoke();
14}

This would not make sense if events were just normal public delegates.

Why Delegates Still Matter

Events are not a replacement for delegates in every scenario. If you want to pass executable behavior around freely, a delegate is appropriate.

Use a delegate when the consumer should be able to call it. Use an event when the consumer should only subscribe.

That distinction keeps APIs honest about who owns execution.

Common Pitfalls

The biggest mistake is declaring a public delegate field when you really mean an event. That gives callers power you did not intend to expose.

Another mistake is thinking event adds a new runtime mechanism unrelated to delegates. It does not. Events are still delegate-based; the keyword mainly restricts how outside code can interact with them.

Developers also sometimes forget that the containing class is still responsible for safe invocation. The event keyword does not remove the need for null checks or thread-awareness.

Finally, do not overuse custom event accessors unless you actually need extra logic. The normal compiler-generated form is simpler and easier to maintain.

Summary

  • 'event exists to restrict outside code to subscription and unsubscription.'
  • A plain public delegate can be invoked, replaced, or cleared by any caller.
  • Events preserve encapsulation by letting only the publisher raise the notification.
  • C# events still use delegates underneath, but with tighter access rules.
  • Choose event when callers should observe behavior, not control it.

Course illustration
Course illustration

All Rights Reserved.