Lesson 6 of 45 intermediate

Delegates, Func, Action and Events

Passing a method around like a value, and telling others when something happens

Guess first

Pick an answer. You will see if you were right at the end of the lesson.

What is the difference between Func<int, bool> and Action<int>?

Open interactive version (quiz + challenge)

Real-world analogy

Think of a phone number written on a sticky note. The note is not the person, but it lets you call whoever it points to. You can hand the note to a friend, and they can make the call later. A delegate is the sticky note for a method. An event is a mailing list: people sign up, and when something happens the owner calls everyone on the list.

What is it?

A delegate is a type that represents a method. A method can then be stored in a variable, passed as an argument and called later. Func and Action are ready-made delegate types, and a lambda is the short way to write one. An event is a controlled delegate for publish and subscribe. The owner raises it, and others can only subscribe or unsubscribe.

Real-world relevance

An order service raises OrderPlaced. An email sender, a stock updater and an audit logger each subscribe, and the service knows none of them. A classic memory leak is a long-lived singleton that raises an event and a short-lived page that never unsubscribes.

Key points

Quick check

Answer from memory — scrolling up first makes it stick less.

Why use an event instead of a public Action field?

A short-lived object subscribes to a long-lived singleton event and never unsubscribes. What happens?

Code example

public record Order(int Id, decimal Total);

public class OrderBook
{
    private readonly List<Order> _orders = new();
    public event EventHandler<Order>? OrderPlaced;

    public void Place(Order order)
    {
        _orders.Add(order);
        OrderPlaced?.Invoke(this, order);
    }

    public IEnumerable<Order> Find(Func<Order, bool> filter) => _orders.Where(filter);
}

public class Mailer
{
    public void OnOrderPlaced(object? sender, Order order) =>
        Console.WriteLine($"Email sent for order {order.Id}");
}

public static class Demo
{
    public static void Run()
    {
        var book = new OrderBook();
        var mailer = new Mailer();
        book.OrderPlaced += mailer.OnOrderPlaced;
        book.Place(new Order(1, 250m));
        var big = book.Find(o => o.Total > 100m).ToList();
        book.OrderPlaced -= mailer.OnOrderPlaced;
    }
}

Line-by-line walkthrough

  1. 1. OrderPlaced is an event. Outside code can use += and -= on it, but cannot call it.
  2. 2. Place adds the order, then raises the event with Invoke. The question mark skips the call if nobody subscribed.
  3. 3. Find takes a Func of Order to bool. The caller decides what counts as a match.
  4. 4. Mailer.OnOrderPlaced is a normal method whose shape matches the event handler.
  5. 5. In Demo.Run, += subscribes the mailer, so Place now causes an email message.
  6. 6. The lambda in Find is a nameless method. The last -= removes the subscription so the mailer can be freed.

Spot the bug

public class DashboardWidget
{
    public DashboardWidget(OrderBook book)
    {
        book.OrderPlaced += Refresh;
    }

    private void Refresh(object? sender, Order order) { /* update screen */ }
}

// book is a singleton; a new widget is created on every page visit
Need a hint?
Who holds a reference to each DashboardWidget after the page is closed?
Show answer
The singleton book keeps a reference to every widget through its event list. No widget can be garbage collected, and each one keeps reacting. Fix: unsubscribe with <code>book.OrderPlaced -= Refresh</code> when the widget is disposed, for example in IDisposable.Dispose.

Explain like I'm 5

A delegate is like a note with a friend's phone number on it. An event is a group chat: people join, and when news happens everyone in the chat gets a message. If you never leave the chat, you keep getting messages forever.

Fun fact

Every delegate in C# is actually a list of methods. When you use += on an event you add one more method to that list. That is why one event can notify many subscribers.

Hands-on challenge

Say it out loud in 30 seconds: "What is a delegate, how is an event different, and how can an event cause a memory leak?" Then check: did you mention a method as a value, subscribe and unsubscribe only, and the publisher holding a reference?

More resources

Open interactive version (quiz + challenge) Download the whole course as PDF / Word

Lock it in

← Back to course: .NET + React Playbook