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.