# Delegates and Events

## 1. Delegates: Type-Safe Function References

A **delegate** is a type whose instances point at a method with a specific signature.

```csharp
delegate int BinaryOp(int a, int b);

static int Add(int a, int b) => a + b;

BinaryOp op = Add;          // method group conversion
int r = op(2, 3);           // 5 — same as op.Invoke(2, 3)
```

Delegates are objects: you can store them in fields, pass them as arguments and return them.

## 2. Built-in Generic Delegates

You rarely declare custom delegate types today:

| Type | Signature | Example |
|---|---|---|
| `Action` / `Action<T1..T16>` | returns `void` | `Action<string> log = Console.WriteLine;` |
| `Func<T1..T16, TResult>` | returns `TResult` (last type arg) | `Func<int, int, int> add = (a, b) => a + b;` |
| `Predicate<T>` | returns `bool` | `Predicate<int> isEven = n => n % 2 == 0;` |

Custom delegates still make sense when the name documents intent (`EventHandler<TEventArgs>`, `RequestDelegate` in ASP.NET Core) or when you need `ref`/`out` parameters.

## 3. Lambdas and Closures

```csharp
Func<int, Func<int, int>> makeAdder = x => y => x + y;
var add5 = makeAdder(5);
add5(10); // 15

int counter = 0;
Action inc = () => counter++;   // captures the VARIABLE, not its value
inc(); inc();
Console.WriteLine(counter);     // 2
```

## 4. Multicast Delegates

Every delegate can hold an **invocation list**. `+=` appends, `-=` removes the last matching entry.

```csharp
Action notify = () => Console.WriteLine("email");
notify += () => Console.WriteLine("sms");
notify();          // email, sms (in subscription order)
```

For a non-`void` multicast delegate, invoking it returns **only the last** handler's result. And if one handler throws, the remaining handlers **don't run**.

## 5. Events: Delegates With Access Control

```csharp
public class OrderService
{
    public event EventHandler<OrderPlacedEventArgs>? OrderPlaced;

    public void Place(Order order)
    {
        // ... save ...
        OrderPlaced?.Invoke(this, new OrderPlacedEventArgs(order.Id));
    }
}

public record OrderPlacedEventArgs(int OrderId);   // EventArgs subclass is optional in modern .NET

var svc = new OrderService();
svc.OrderPlaced += (sender, e) => Console.WriteLine($"placed {e.OrderId}");
```

The `event` keyword restricts what **outside code** can do:

| Operation | public delegate field | public `event` |
|---|---|---|
| `+=` / `-=` subscribe | ✅ | ✅ |
| `= handler` (replace everyone) | ✅ | ❌ compile error |
| `svc.OrderPlaced(...)` (raise) | ✅ | ❌ only the declaring class |
| `= null` (wipe subscribers) | ✅ | ❌ |

## 6. The Standard Event Pattern

- Signature `void Handler(object? sender, TEventArgs e)` — use `EventHandler` / `EventHandler<T>`.
- Raise through a `protected virtual void OnXxx(TEventArgs e)` method so subclasses can hook in.
- Copy-then-invoke (`OrderPlaced?.Invoke(...)`) is thread-safe against a concurrent unsubscribe because delegates are **immutable** — `?.` reads the field once.

## Under the Hood: What a Delegate and an Event Compile To

A delegate type compiles to a sealed class deriving from `System.MulticastDelegate`, with a runtime-implemented `Invoke` method. Each instance stores:

- `_target` — the object to call the method on (`null` for static methods; for a capturing lambda, an instance of a compiler-generated **closure class** that holds the captured variables as fields)
- `_methodPtr` — the code address of the method
- `_invocationList` — an array of delegates when there's more than one subscriber

`+=` calls `Delegate.Combine`, which **allocates a new delegate** with a new array — delegates never mutate in place. That's why a copy you already read stays stable while other threads subscribe or unsubscribe.

A field-like `event` compiles to a private delegate field plus `add_X`/`remove_X` accessor methods that perform `Combine`/`Remove` in a lock-free `Interlocked.CompareExchange` loop. Outside the class, `x.OrderPlaced += h` becomes `x.add_OrderPlaced(h)` — there is no accessor for raise or assign, which is how the compiler enforces the table above.

![An event exposes only add and remove accessors; the private backing field holds an immutable multicast delegate whose invocation list points at each handler's target object and method](diagrams/01-multicast-invocation-list.svg)

## Common Pitfalls

1. **Memory leaks via events.** The publisher's invocation list holds a strong reference to every subscriber's `_target`. A long-lived publisher (static event, singleton) keeps short-lived subscribers alive forever unless they `-=`.
2. **Unsubscribing a lambda.** `x.E -= () => Foo();` creates a *new* delegate that never matches. Store the handler in a variable to remove it.
3. **Raising without a null check** — an event with no subscribers is `null`. Use `?.Invoke`.
4. **Closures over loop variables** in old `for` loops capture one shared variable. (`foreach` gets a fresh variable per iteration since C# 5; `for` does not.)
5. **Relying on return values or exception flow of multicast delegates** — only the last return value survives and one throwing handler stops the rest. Iterate `GetInvocationList()` when every handler must run.

## Quick Reference

| Need | Use |
|---|---|
| callback returning nothing | `Action<T>` |
| callback returning a value | `Func<T, TResult>` |
| condition | `Predicate<T>` / `Func<T,bool>` |
| publish notifications | `public event EventHandler<TArgs>? Xxx;` |
| raise safely | `Xxx?.Invoke(this, args);` |
| run every handler even if one throws | `foreach (var d in Xxx.GetInvocationList())` |
