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.