12.2 Multicasting

At times, it is desirable to call two (or more) implementing methods through a single delegate. This becomes particularly important when handling events (discussed later in this chapter).

The goal is to have a single delegate that invokes more than one method. This is different from having a collection of delegates, each of which invokes a single method. In the previous example, the collection was used to order the various delegates. It was possible to add a single delegate to the collection more than once, and to use the collection to reorder the delegates to control their order of invocation.

With multicasting, you create a single delegate that will call multiple encapsulated methods. For example, when a button is pressed, you might want to take more than one action. You could implement this by giving the button a collection of delegates, but it is cleaner and easier to create a single multicast delegate.

Two delegates can be combined with the addition operator (+). The result is a new multicast delegate that invokes both of the original implementing methods. For example, assuming Writer and Logger are delegates, the following line will combine them and produce a new multicast delegate named myMulticastDelegate:

myMulticastDelegate = Writer + Logger;

You can add delegates to a multicast delegate using the plus-equals (+=) operator. This operator adds the delegate on the right side of the operator to the multicast delegate on the left. For example, assuming Transmitter and myMulticastDelegate are delegates, the following line adds Transmitter to myMulticastDelegate:

myMulticastDelegate += Transmitter;

To see how multicast delegates are created and used, let's walk through a complete example. In Example 12-3, you create a class called MyClassWithDelegate that defines a delegate that takes a string as a parameter and returns void:

public delegate void StringDelegate(string s);

You then define a class called MyImplementingClass that has three methods, all of which return void and take a string as a parameter: WriteString, LogString, and TransmitString. The first writes the string to standard output, the second simulates writing to a log file, and the third simulates transmitting the string across the Internet. You instantiate the delegates to invoke the appropriate methods:

Writer("String passed to Writer\n");
Logger("String passed to Logger\n");
Transmitter("String passed to Transmitter\n");

To see how to combine delegates, you create another delegate instance:

MyClassWithDelegate.StringDelegate myMulticastDelegate;

and assign to it the result of "adding" two existing delegates:

myMulticastDelegate = Writer + Logger;

You add to this delegate an additional delegate using the += operator:

myMulticastDelegate += Transmitter;

Finally, you selectively remove delegates using the -= operator:

DelegateCollector -= Logger;
Example 12-3. Combining delegates
namespace Programming_CSharp
{
   using System;

   public class MyClassWithDelegate
   {
      // the delegate declaration
      public delegate void StringDelegate(string s);

   }

   public class MyImplementingClass
   {
      public static void WriteString(string s)
      {
         Console.WriteLine("Writing string {0}", s);
      }

      public static void LogString(string s)
      {
         Console.WriteLine("Logging string {0}", s);
      }

      public static void TransmitString(string s)
      {
         Console.WriteLine("Transmitting string {0}", s);
      }

   }


   public class Test
   {
      public static void Main( )
      {
         // define three StringDelegate objects
         MyClassWithDelegate.StringDelegate 
            Writer, Logger, Transmitter;

         // define another StringDelegate
         // to act as the multicast delegate
         MyClassWithDelegate.StringDelegate 
            myMulticastDelegate;

         // Instantiate the first three delegates, 
         // passing in methods to encapsulate
         Writer = new MyClassWithDelegate.StringDelegate(
            MyImplementingClass.WriteString);
         Logger = new MyClassWithDelegate.StringDelegate(
            MyImplementingClass.LogString);
         Transmitter = 
            new MyClassWithDelegate.StringDelegate(
            MyImplementingClass.TransmitString);

         // Invoke the Writer delegate method
         Writer("String passed to Writer\n");

         // Invoke the Logger delegate method 
         Logger("String passed to Logger\n");

         // Invoke the Transmitter delegate method
         Transmitter("String passed to Transmitter\n");
            
         // Tell the user you are about to combine
         // two delegates into the multicast delegate
         Console.WriteLine(
            "myMulticastDelegate = Writer + Logger");

         // combine the two delegates, the result is
         // assigned to myMulticast Delegate
         myMulticastDelegate = Writer + Logger;

         // Call the delegated methods, two methods
         // will be invoked
         myMulticastDelegate(
            "First string passed to Collector");

         // Tell the user you are about to add
         // a third delegate to the multicast
         Console.WriteLine(
            "\nmyMulticastDelegate += Transmitter");

         // add the third delegate
         myMulticastDelegate += Transmitter;

         // invoke the three delegated methods
         myMulticastDelegate(
            "Second string passed to Collector");

         // tell the user you are about to remove
         // the logger delegate
         Console.WriteLine(
            "\nmyMulticastDelegate -= Logger");

         // remove the logger delegate
         myMulticastDelegate -= Logger;

         // invoke the two remaining 
         // delegated methods
         myMulticastDelegate(
            "Third string passed to Collector");
      }
   }
}

Output:
Writing string String passed to Writer

Logging string String passed to Logger

Transmitting string String passed to Transmitter

myMulticastDelegate = Writer + Logger
Writing string First string passed to Collector
Logging string First string passed to Collector

myMulticastDelegate += Transmitter
Writing string Second string passed to Collector
Logging string Second string passed to Collector
Transmitting string Second string passed to Collector

myMulticastDelegate -= Logger
Writing string Third string passed to Collector
Transmitting string Third string passed to Collector 

In the Test portion of Example 12-3, the delegate instances are defined and the first three (Writer, Logger, and Transmitter) are invoked. The fourth delegate, myMulticastDelegate, is then assigned the combination of the first two and it is invoked, causing both delegated methods to be called. The third delegate is added, and when myMulticastDelegate is invoked, all three delegated methods are called. Finally, Logger is removed, and when myMulticastDelegate is invoked, only the two remaining methods are called.

The power of multicast delegates is best understood in terms of events, discussed in the next section. When an event such as a button press occurs, an associated multicast delegate can invoke a series of event handler methods that will respond to the event.



    Part I: The C# Language