Multicast Delegates
Vaibhav • September 11, 2025
In the previous article, we explored how to declare and use delegates - a powerful feature in C# that allows methods to be treated as data. Now we’re ready to take that concept further with multicast delegates. These special delegates can hold references to more than one method, and when invoked, they call all the methods in their invocation list - in order.
Multicast delegates are especially useful in event-driven programming, logging systems, and notification frameworks. They allow you to wire up multiple handlers to a single delegate and execute them all with a single call. In this article, we’ll explore how multicast delegates work, how to combine and remove methods, what happens when exceptions are thrown, and how return values are handled.
What is a Multicast Delegate?
A multicast delegate is a delegate that holds references to more than one method. When you invoke it, all the
methods in its list are executed in the order they were added. This behavior is built into the base Delegate
class in .NET.
You can create a multicast delegate by using the +=
operator to add methods to
an existing delegate instance. Let’s look at a simple example:
public delegate void Notifier(string message);
void NotifyConsole(string msg) => Console.WriteLine("Console: " + msg);
void NotifyFile(string msg) => Console.WriteLine("File: " + msg); // Simulated
Notifier notify = NotifyConsole;
notify += NotifyFile;
notify("System update");
// Output:
// Console: System update
// File: System update
Here, the delegate notify
holds two methods. When invoked, both are called in
sequence. This is the essence of multicast behavior.
Combining Delegates
You can combine delegates using the +=
operator. Each method added becomes part
of the invocation list. The order matters - methods are called in the order they were added.
notify += AnotherMethod;
notify += YetAnotherMethod;
This pattern is common in event systems, where multiple listeners respond to a single trigger. For example, a button click might notify the UI, log the action, and send telemetry - all through a multicast delegate.
Internally, multicast delegates are implemented as a linked list of delegate instances. When you invoke the delegate, the runtime walks through the list and calls each method.
Removing Methods from a Delegate
You can remove methods from a multicast delegate using the -=
operator. This
updates the invocation list and ensures the removed method is no longer called.
notify -= NotifyFile;
notify("After removal");
// Output:
// Console: After removal
Only NotifyConsole
is called now. This dynamic behavior makes multicast
delegates ideal for systems where handlers may be added or removed at runtime.
Return Values in Multicast Delegates
If your delegate has a return type, only the result of the last method in the invocation list is returned. All other return values are ignored. This is an important nuance to understand.
public delegate int Calculator(int x, int y);
int Add(int a, int b)
{
Console.WriteLine("Add called");
return a + b;
}
int Multiply(int a, int b)
{
Console.WriteLine("Multiply called");
return a * b;
}
Calculator calc = Add;
calc += Multiply;
int result = calc(2, 3); // Output: Add called, Multiply called
Console.WriteLine(result); // Output: 6 (from Multiply)
Even though both methods are called, only the result of Multiply
is returned.
This makes multicast delegates unsuitable for scenarios where you need to aggregate or combine return values.
Note: If you need to collect all return values, consider invoking each method manually or using a loop over the invocation list.
Accessing the Invocation List
You can inspect the methods attached to a multicast delegate using the GetInvocationList()
method. This returns an array of individual delegates, which
you can iterate over and invoke separately.
foreach (Delegate d in notify.GetInvocationList())
{
d.DynamicInvoke("Manual call");
}
This gives you full control over how each method is called, and allows you to handle return values, exceptions, or timing individually.
Exception Handling in Multicast Delegates
If one of the methods in a multicast delegate throws an exception, the invocation stops immediately - and the exception is propagated to the caller. This means later methods in the list are not called.
void SafeNotify(string msg) => Console.WriteLine("Safe: " + msg);
void FaultyNotify(string msg) => throw new Exception("Oops");
Notifier notify = SafeNotify;
notify += FaultyNotify;
notify += SafeNotify;
notify("Test"); // Exception thrown, second SafeNotify not called
To ensure all methods are called even if one fails, you must manually invoke each delegate in the invocation list and wrap each call in a try-catch block.
foreach (Delegate d in notify.GetInvocationList())
{
try
{
d.DynamicInvoke("Safe test");
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
Always handle exceptions when invoking multicast delegates, especially in event systems. One faulty handler should not prevent others from running.
Multicast Delegates in Events
Events in C# are built on top of multicast delegates. When you declare an event, you’re actually creating a delegate field that supports multiple subscribers. Each subscriber adds a method to the delegate’s invocation list.
public event Notifier OnNotify;
OnNotify += NotifyConsole;
OnNotify += NotifyFile;
OnNotify?.Invoke("Event triggered");
This pattern is used throughout .NET - from UI frameworks to custom business logic. Events are multicast by
default, and the +=
and -=
operators manage
the subscription list.
Multicast Delegates vs Collections of Delegates
You might wonder - why not just use a List<Action>
or similar collection
to store methods? While that works, multicast delegates offer built-in syntax, automatic chaining, and
integration with events. They’re optimized for scenarios where you want to invoke multiple methods with a single
call.
Collections give you more control over iteration, error handling, and return values - but require more boilerplate. Multicast delegates are simpler and more idiomatic in C#.
Performance Considerations
Multicast delegates are efficient for small invocation lists. The runtime walks the list and calls each method directly. However, if your list grows large or methods are expensive, consider profiling the performance.
Also be mindful of memory - each method reference keeps its target object alive. If you attach long-lived delegates to short-lived objects, you may inadvertently prevent garbage collection.
Delegates are immutable. When you add or remove a method, a new delegate instance is created. This makes them thread-safe for read operations, but you should still synchronize updates in multithreaded scenarios.
Summary
Multicast delegates extend the power of delegates by allowing multiple methods to be invoked with a single call. They’re ideal for event handling, logging, and notification systems. You learned how to combine and remove methods, handle exceptions, inspect the invocation list, and understand return value behavior.
While multicast delegates are simple to use, they come with important nuances - especially around exceptions and return values. By understanding these details, you can build robust, flexible systems that respond to events and changes gracefully.
In the next article, we’ll explore Anonymous Methods - a way to define inline method bodies without declaring a separate named method. This opens the door to more expressive and concise code, especially when working with delegates and events.