All instances of Delegate are MulticastDelegates, a subclass of the Delegate class. Specifically, multicasting refers to a delegate having more than one procedure address in its invocation list.
When you define multiple event handlers for a single event, when the event occurs each event handler is called. (This behavior is probably implemented internally by iterating through the invocation listaccessible by the method GetInvocationListand calling each method in the list, passing the required arguments.) Suppose you have a Button control named Button1. Defining two methods with the Handles Button1.Click clause would cause the Button1.Click event to multicast the event to both methods with the Handles Button1.Click clause. Procedures are invoked in the order in which they appear in the invocation list. If you write the event handlers at design time, they are added to the invocation list in the order in which they appear in the code.
Avoid writing event handlers in such a way as to depend on their invocation order.
Listing 9 demonstrates two event handlers responding to a single click event. The first event handler, Button1_Click, was generated by double-clicking on the button control. The second event handler was added explicitly by defining a subroutine that matched the signature of the EventHandler and typing the Handles clause manually.
Listing 9Two Event Handlers for the Same Event
1: Private Sub Button_Click(ByVal sender As System.Object, _ 2: ByVal e As System.EventArgs) Handles Button1.Click 3: 4: MsgBox("Button click added manually") 5: 6: End Sub 7: 8: Private Sub Button1_Click(ByVal sender As System.Object, _ 9: ByVal e As System.EventArgs) Handles Button1.Click 10: 11: MsgBox("Button1_Click added by the form designer") 12: 13: End Sub
Lines 8 through 13 were added by the Form designer when I double-clicked on the button. Lines 1 through 6 were typed. The Handles Button1.Click clause is all that distinguishes these two methods from being regular methods and makes them event handlers for Button1's Click event. (You can call Button_Click and Button1_Click directly, too.)
The Handles clause is used to define event handlers at code-time. When your program is running, you need to use AddHandler and RemoveHandler to add or remove additional procedures to or from a Delegate's invocation list. The form of the call is AddHandler Object As Event, Object As Delegate, where the first parameter is an event object and the second parameter is a reference to a Delegate (see Listing 10 for an example).
I asked a couple of the .NET programmers about the absence of parentheses for the AddHandler and RemoveHandler statements. (Recall that everything else uses the parentheses in Visual Basic .NET.) No concise answer was offered. For consistency, perhaps parentheses will ultimately be required for AddHandler and RemoveHandler.
Better than adding parentheses to AddHandler and RemoveHandler, if operator overloading were supported, you could write delegate = addressOf procedure, which is much more intuitive.
Listing 10Adding and Removing a handler in an Invocation List
1: Private Sub Button_Click(ByVal sender As System.Object, _ 2: ByVal e As System.EventArgs) Handles Button1.Click 3: 4: MsgBox("Button click added manually") 5: 6: End Sub 7: 8: Private Sub Button1_Click(ByVal sender As System.Object, _ 9: ByVal e As System.EventArgs) Handles Button1.Click 10: 11: MsgBox("Button1_Click added by the form designer") 12: 13: End Sub 14: 15: Private Sub RemoveDelegate() 16: 17: Dim ADelegate As EventHandler = AddressOf Button_Click 18: RemoveHandler Button1.Click, ADelegate 19: 20: End Sub
Lines 1 through 13 represent the static code typed at design time. Line 17 declares a Delegate and initializes it to the address of Button_Click. RemoveHandler instructs the application to remove the delegate whose address is equal to Button_Click from the Click Delegateor EventObject. In other words, Click is a Delegate and Button_Click is removed from Click's invocation list. If we wanted to add Button_Click back to Click's invocation list, we would write AddHandler Button1.Click, ADelegate.
Using the Combine Method
An alternative way to add procedure addresses to a Multicast Delegate is to use the Combine method. Combine is overloaded so it can be called with two different parameter signatures, either an array of Delegate or two individual instances of Delegate. In either case it returns a Delegate whose invocation list contains all of the delegates passed to it (all delegates in the array or the two individual delegates). Assume that you have Delegate instances D1, D2, and D3 defined as EventHandler Delegates as follows:
Dim D1, D2, D3 As EventHandler
Also, assume that you initialized D2 and D3 to the address of a suitable subroutine. Calling the Combine method passing D2 and D3 as arguments would return a Delegate whose invocation list contained the procedural address of D2's and D3's combined invocation lists:
D1 = System.Delegate.Combine(D2, D3)
If D2 and D3 each contained one procedure in their invocation lists, D1 would contain the sum of the number of elements, or two in this instance. Keep in mind that a Delegate is a class but basically represents a procedural type. To manually invoke all of the procedures in the invocation list of D1, write D1 as if you were calling a procedure:
D1( Nothing, Nothing )
In this example Nothing, Nothing represents passing nothing for both the Object and EventArgs parameters of an EventHandler Delegate. To remove a Delegate from the invocation list, call the Shared method Remove. For example, to remove the D3 methods from D1's invocation list, write this:
D1 = System.Delegate.Remove( D1, D3 )
D1 now contains only the procedures in D2's invocation list. Calling Remove with Delegates not previously combined performs no action and doesn't raise an error.
Using Multicast Delegates
Multicast delegates support adding the address of one or more procedures to the invocation list of a delegate. For example, you might have behaviors that are only invoked when an application is in a specific state. In such a state, you can invoke several procedures when a single event occurs. With multicasting capability, you can layer in or remove code that responds to an event from an invocation list, much as a symphony conductor layers in musical instruments.
You are more likely to singlecast regularly than you are to multicast, but multicasting is available to you if you need it. In addition to thinking of multicasting delegates as a means of responding to events, you can also think of delegates as a way to define a list of procedures that all operate on the same object. You have one object and many operations; none, some, or all of which are invoked on the object depending on the program's state. A delegate can be used to begin each step in processing an object's event, and the steps taken are regulated by adding or removing additional delegates in the invocation list.