Now you know how to consume and produce events, and we'll see many examples of these throughout this book. But you may be asking yourself, "What is the big deal about delegates?"
For the most part, after reading the earlier sections in this chapter you know everything you need to know. However, there is an important chunk of information we still need to cover. Delegates are actually classes. For years we have had function pointers ”also called procedural types ” but these implementations were not classes. They were simple instances of single function pointers. The result was that every programmer had to write code to determine whether the function pointer was assigned to something, and if you wanted to stored multiple values for the function pointer, you had to track these multiple pointers with custom code too.
Delegates are classes that contain these capabilities, thus eliminating the need for developers to roll their own. Delegate classes can determine whether something is actually assigned to the delegate by keeping an internal list. The algorithm can be understood as follows : For every callback function in the internal list, call the function.
In addition to supporting callback functions, delegates can check to see whether any function pointers are associated with a particular delegate by maintaining an internal list, and delegates can use that list to multicast. Multicasting simply means calling all the function pointers in the invocation list. The result is that one delegate can actually invoke many functions.
As Paul Harvey would say, "That's the rest of the story."
Why Are Delegates So Important?
There is so much hubbub about delegates because they are central to the Windows operating system and .NET itself. Delegates are used to support the event-driven model in Windows. Delegates are used to support dynamic method behaviors (as demonstrated earlier in this chapter), and delegates are used to register worker functions with asynchronous and multithreaded behavior.
In a nutshell , if you don't understand delegates, then dynamic methods , asynchronous behavior, thread pooling, and multithreading will not be available to you.
Delegates are defined by preceding the signature of a method with the keyword Delegate . To define a delegate, type the access modifier, the keyword Delegate , and a procedure heading. The following excerpt from Listing 3.4 demonstrates the grammar.
6: Private Delegate Function Compare(ByVal lhs As Object, _ 7: ByVal rhs As Object) As Boolean
By quick inspection you can determine that the only difference between a method heading and a delegate is the inclusion of the keyword Delegate . While events must be subroutines, delegates that are not used to handle events can be subroutines or functions.
You can use the constructor syntax to create a new instance of a delegate type, or you can create a delegate implicitly by taking the address of a method that has the signature of a particular delegate. For example, when a method expects an event handler and you take the address of a method that has the same signature as the event handler, you will be implicitly creating an instance of an event handler delegate. Here are two examples yielding the same result.
Sort(Items, AddressOf Less) Sort(Items, New Compare(AddressOf Less))
The first line is taken from line 45 in Listing 3.4 and implicitly creates an instance of the Compare delegate. The second line is the more verbose example, which demonstrates constructing an instance of the Compare delegate and initializing the delegate with the address of a procedure that has the signature of the delegate. You can use the first form to create the delegate as long as the compiler can determine the delegate type by context. If you get an error related to a delegate and you are using the abbreviated code to construct the delegate, switch to the verbose syntax.
When you are performing visual programming tasks , you will likely be using existing delegates like the EventHandler delegate. For more advanced tasks you may need to define your own delegates or use other existing delegates, such as the WaitCallback delegate used with the ThreadPool class for multithreaded operations.