Back to Blog
Tech

Events, Delegates, and Handlers: The Restaurant Analogy That Finally Made It Click

Stop thinking about function pointers. Start thinking about bells and notifications. A restaurant analogy that makes C# events actually make sense.

Events, Delegates, and Handlers: The Restaurant Analogy That Finally Made It Click

The Problem

Last week I was wiring up an EventCallback in Blazor. The child component needed to tell the parent "hey, this task just changed." I copied the pattern from another component, it worked, and I moved on.

But I didn't understand it. Not really.

Every explanation I've read starts with "a delegate is a type-safe function pointer" and my brain immediately checks out. Function pointer? My eyes glaze over.

I can copy patterns. I can make things work. But I've never truly felt it click.

So let's try something different.

Forget Code. Let's Talk About Restaurants.

You walk into a restaurant. You sit down. You want food.

Here's what you don't do: walk into the kitchen, grab the chef by the shoulders, and say "MAKE ME A BURGER NOW."

Instead, you:

  1. Tell the waiter what you want
  2. Go back to your conversation
  3. The waiter comes back when your food is ready

That's it. That's events and delegates. That's the whole thing.

You don't go to the kitchen. The kitchen comes to you when it's ready.

The Three Players

Let's name our players:

Restaurant Term C# Term What It Does
The menu (what you CAN order) Delegate Defines the shape of a message
The bell the kitchen rings Event The signal that something happened
What you do when you hear the bell Handler Your response to that signal

The Delegate: It's Just a Menu

Here's where most explanations fail. They say "a delegate is a type that represents a method signature."

Boring. Abstract. Useless.

Let's try again.

A delegate is a menu.

A menu doesn't contain food. It describes food. It says "a burger comes with a patty, bun, and toppings."

// This doesn't DO anything. It describes what a "food order" looks like.
public delegate void FoodOrder(string item, int quantity);

That's a delegate. It says: "Any message of type FoodOrder will include a string and an int."

It's not a method. It's not a function. It's a description of what methods must look like to participate in this conversation.

You can't eat a menu. But you can't order without one.

The Event: It's Just a Bell

The kitchen has a bell. When food is ready, they ring it.

The bell doesn't care who's listening. The bell doesn't care what you do when you hear it. The bell just... rings.

public class Kitchen
{
    // The bell. Anyone can listen for it.
    public event FoodOrder OnFoodReady;

    public void CookFood(string item, int quantity)
    {
        // ... cooking happens ...

        // Ring the bell!
        OnFoodReady?.Invoke(item, quantity);
    }
}

That ? is important. It means "if anyone is listening." If the restaurant is empty and no one subscribed to the bell, it just rings into silence. No crash. No error. Just silence.

The kitchen doesn't know or care who's listening. It just rings the bell.

The Handler: It's What YOU Do

You're sitting at your table. You hear the bell. What do you do?

That's your handler. It's YOUR method. It runs when the event fires.

public class Customer
{
    public void ReactToFoodReady(string item, int quantity)
    {
        Console.WriteLine($"Yay! My {quantity} {item}(s) are ready!");
    }
}

This method matches the delegate signature. String, int, returns void. It fits the menu description.

Putting It Together: Subscribe to the Bell

Here's the magic moment. You tell the kitchen: "Hey, when you ring that bell, call MY method."

var kitchen = new Kitchen();
var me = new Customer();

// Subscribe: "When OnFoodReady rings, call my ReactToFoodReady"
kitchen.OnFoodReady += me.ReactToFoodReady;

// Later, kitchen cooks and rings the bell
kitchen.CookFood("burger", 2);

// Output: "Yay! My 2 burger(s) are ready!"

That += is subscription. You're saying "add me to your notification list."

You don't poll the kitchen asking "is it ready yet?" The kitchen tells YOU.

The Aha Moment: Inversion of Control

Here's what finally clicked for me.

In normal code, I call methods directly:

// I call you
kitchen.CookFood("burger", 2);
string result = kitchen.GetStatus();

With events, it flips:

// You call me (when you're ready)
kitchen.OnFoodReady += me.ReactToFoodReady;

I don't ask "are you done?" I say "tell me when you're done."

That's it. That's the fundamental shift.

You stop pulling information. You start receiving notifications.

Why Does This Matter?

In the Blazor code I was just writing, I had this:

[Parameter] public EventCallback<TaskItem> OnStatusChanged { get; set; }

Before today, that was magic incantations. Now I see it:

  • EventCallback<TaskItem> = A bell that rings with TaskItem info
  • OnStatusChanged = The name of that bell
  • The parent component subscribes = "Call me when status changes"

When the child component does something:

await OnStatusChanged.InvokeAsync(Task);

It's ringing the bell. It's saying "HEY! Something changed! Here's what!"

The child doesn't know or care who's listening. It just rings.

The Pattern Everywhere

Once you see it, you see it everywhere:

  • Button click: The button rings a bell when clicked. You handle it.
  • File download: The downloader rings when complete. You handle it.
  • Timer tick: The timer rings every interval. You handle it.

The publisher (kitchen) doesn't know the subscriber (you).

The subscriber doesn't know when things will happen.

They're connected only by the event - the bell.

Quick Reference

Concept Restaurant Code
Delegate The menu format delegate void FoodOrder(string, int)
Event The bell event FoodOrder OnFoodReady
Handler Your reaction A method matching the delegate signature
Subscribe "Notify me" event += handler
Invoke Ring the bell event?.Invoke(...)

The One Sentence Summary

Events let objects notify other objects that something happened, without knowing or caring who's listening.

Kitchen rings bell. Customer reacts. Neither knows the other's implementation details.

That's loose coupling. That's events. That's why we use them.


Bonus: EventCallback vs event in Blazor

In Blazor, you'll see EventCallback<T> instead of regular event. Why?

It's the same concept - still a bell - but EventCallback is optimized for Blazor's rendering system. When you invoke it, Blazor knows to re-render the component that's listening.

Think of it as a "smart bell" that also turns on the lights in your section of the restaurant so the waiter can find you.

// Regular C# event
public event Action<TaskItem> OnStatusChanged;

// Blazor's version - same idea, but UI-aware
[Parameter] public EventCallback<TaskItem> OnStatusChanged { get; set; }

Same bell. Same subscription model. Just smarter about UI updates.


This is part of the ELI5 series—technical concepts explained without the jargon. For more on Blazor component communication, see the code in context throughout this post.

The key shift: stop thinking about "function pointers" and start thinking about bells and notifications. You're not calling methods—you're subscribing to announcements. Once that clicks, you'll see the pattern everywhere.

Comments

More in Tech