Phase 10 of 30 · Topic 10.1

MulticastDelegate Memory Layout & Invocation List

1Concept

Every delegate instance inherits from `MulticastDelegate`, holding an `_target` object pointer, a `_methodPtr` function pointer, and an optional `_invocationList` array when combining multiple listeners with `+=`.

2Architecture Diagram

MulticastDelegate Object Layout:
┌──────────────────────────┬──────────────────────────┬──────────────────────────┐
│  _target (Object ptr)    │  _methodPtr (Func ptr)   │ _invocationList (Array)  │
└──────────────────────────┴──────────────────────────┴──────────────────────────┘
   ▲ Target instance (or null) ▲ Native code address      ▲ Holds chained delegate listeners

3Code Example

C# 13 & .NET 9
using System;

public delegate void NotificationHandler(string message);

public class DelegateInternalsDemo
{
    public static void Main()
    {
        NotificationHandler handler1 = msg => Console.WriteLine($"Console: {msg}");
        NotificationHandler handler2 = msg => Console.WriteLine($"AuditLog: {msg}");

        // Combine into MulticastDelegate invocation list
        NotificationHandler combined = handler1 + handler2;
        combined("Server Started on Port 5000");

        Console.WriteLine($"Total Invocation List Listeners: {combined.GetInvocationList().Length}");
    }
}

4Expected Output

Console: Server Started on Port 5000
AuditLog: Server Started on Port 5000
Total Invocation List Listeners: 2

5Key Takeaways

  • Delegates are immutable; `+=` creates a new `MulticastDelegate` instance.
  • If an exception is thrown in a multicast chain, subsequent listeners are skipped unless manually iterated via `GetInvocationList()`.
  • Use `Action<T>` and `Func<T, TResult>` instead of custom delegate declarations.