If you are comfortable working with methods (Sub Procedures and Functions) in Visual Basic, then you should have no problem understanding the concept of Delegates.
Delegates are basically pointers to methods that have the exact same signature (or “shape”) as the method they point to. They are strongly-typed, and can be invoked (or “called”) at any time. Delegates can point to 1 method, or multiple methods, and can be invoked either synchronously or asynchronously.
In the .NET Framework, there are many place where Delegates are used. Examples include generic lists (System.Collections.Generic.List(Of Type)) which have Sort and Find methods that use them, every Event uses a Delegate (for example, a “button_click()” event), and the new LINQ architecture uses them extensively.
There are three pieces of information that are maintained by a Delegate, and they are: The address (or name) of the method they are pointing to, the arguments that are passed to the method, and the return type of the method (if there is one).
To create a Delegate, you must declare it at the Member (class) level, using the Delegate Keyword. Additionally, you must specify whether the Delegate is going to point to a Sub Procedure or a Function. Just like any variable, you can name the Delegate whatever you want. And as mentioned, you must define the signature of the Delegate (including parameters and return type).
Here’s an introductory example of creating a Delegate that will point to a Sub Procedure with no parameters:
As mentioned, this Delegate can be used to point to any Sub Procedure that has no arguments. Here is an example:
As a note, the method that the delegate is pointing to does not have to have the same (or similar) name as the delegate, and the delegate can point to any method that has the exact same signature as the delegate (which is a Sub Procedure with no arguments in this case).
To use a Delegate, you have to create a variable that is of the delegate type, which in this case is the “PrintHelloWorldDelegate” delegate type, and tell it what method it is going to “point to”. To demonstrate this, we will add a new method called “Print”, which will be called to perform the printing.
The complete class now looks like this:
Delegates with Parameters
As mentioned, Delegates can point to methods that have parameters. But in order for Delegates to be able to point to methods that have parameters, they must be declared with the exact same list of parameters that the method they are going to point to has.
As an example, here is a delegate that will point to a Sub Procedure that has 1 String parameter:
To use PrintStringDelegate in the example above, we need to create a variable of the PrintStringDelegate type, and assign it the address of the PrintString Sub Procedure, then invoke (call) it.
Here is another example of a delegate that will point to a Sub Procedure that has 1 Integer parameter:
And again, an example of how to invoke (call) it:
Remember that Delegates can take any number of parameters, they are not just limited to 1. So let’s take a look at a Delegate that has multiple parameters:
And again, here’s an example of how to invoke (call) it:
So all that is pretty simple? Ok, let’s take a look at working with Delegates that have return types.
Delegates with Parameters and Return Types
So far we’ve been working with Delegates that do not have Return Types. These Delegates have been declared using the “Sub” keyword. To create a Delegate that has a Return Type, use the “Function” keyword instead. And, just like a function, add the Return Type at the end of the declaration.
Here’s an example of a delegate that will point to a Function that will add 2 Integers together and return an Integer as a result:
And here’s an example of how to invoke it:
Remember that a Function Delegate does not have to have parameters, but it does have to have a Return Type. The parameters do not have to be of the same type as each other, or as the Return Type.
Ok, so now that we’ve had a chance to look at Delegates, including their parameters and return types, let’s take a look at some examples of using Delegates.
Event Handling with Delegates
The .NET Framework has a generic collection list class called “List(Of Type)” that can be used to store any type of object. Although the list is generic, it is a strongly-typed list that will only store objects of the declared type. So, if you declare your list as “List(Of String)” the list will only store Strings, if you declare you list as “List(Of Integer)” the list will only store Integers, and so on.
Because this type of collection list is so useful for storing objects, it makes it a perfect candidate for inheritance. A derived class can be created, and programmed with a Delegate to provide “call back” event notification to let the caller receive feedback on the status of items that have been added to the list.
The concept of a “call back” delegate is pretty simple. In the easiest since, think of when you send someone an email. When you send the email to them, your email address is included in the email. Then, when they have read the email, (at their convenience) they reply and use the email address to send an email message back to you.
Another example, think of the Button.Click event (which we’ll see an example of later). When the button is clicked, something needs to tell the compiler what “call back” method is supposed to be called (e.g. the “Button1_Click” Sub Procedure): That something is a Delegate.
So here’s an example where the “List(Of Type)” class is inherited by the “MyList(Of T)” class. We will add a Delegate to the class to provide call back event notification when a new item is added to the list:
So, having looked at all of that, let’s see how it works from the caller’s end:
Hopefully that’s easy to understand! If it doesn’t make since, then create a new Windows Forms project, add a Button to Form1, then go to the Form1 code window, highlight all of the code and delete it. Then, copy the code for the Form1 class from the example above and paste it into the code window. Finally, copy the code from the “MyList(Of T)” class and paste it after the Form1’s “End Class” keywords. Run the project and click the button to see the results. If you can set a breakpoint on the “list.Add(1)” line of code, then click the button, program execution will be halted at that line, then you can “step-into” (F11 in VS 10) the method and see how the delegate works.
And if you made it through that example ok, then you’re ready for some more advanced examples…
Examples of using Delegates in .NET
There are many classes in the .NET framework that use Delegates. However, it may not always be obvious that a Delegate is being used.
One plain give-away that a delegate is being used is that the “AddressOf” keyword is used. When used in a Delegate constructor, the “AddressOf” operator assigns the specified method address to the Delegate. When the “AddressOf” statement is used in context, the type of the delegate is determined by the compiler, and an instance of a delegate is created for you.
Having said that, let’s take a look at the generic List(Of Type) class that is located in System.Collections.Generic namespace. There are 2 methods that we can look at: The Sort method, which takes a System.Comparison(Of Type) delegate; and the Find method, which takes a System.Predicate(Of Type) delegate.
Because the List(Of Type) is a generic list, it literally can hold anything, once the Type has been specified. For example, a List(Of String) can store a list of strings, a List(Of Integer) can store a list of integers, a List(Of TextBox) can store a list of TextBoxes, a List(Of Form) can store a list of Forms, and so on. Because the list can store any type, the list wouldn’t have the ability to sort the items in the list, or search for items in the list, because it wouldn’t know how. Therefore, methods such as the Sort and Find method must be programmatically defined. And that’s where delegates come in!
Here’s a Sorting Delegate Example:
Here’s the “SortList” method being called by the “sortListDelegate” delegate:
Here’s a Find Delegate Example:
And here’s the “FindInList” method that is being called by the “findDelegate”:
As mentioned previously, every Event uses a Delegate. An event that you are very familiar with is the Button.Click event. This event too uses a Delegate. Here’s an example, notice the use of “AddressOf” to create the Delegate:
Here’s the Button_Click method that is being invoked by the delegate:
Up to this point we’ve learned that a Delegate has the ability to “point to” a method. When the Delegate is invoked, the method that it “points to” is called. Something we haven’t really discussed is the fact that a Delegate can actually “point to” more than one method at a time, and when it is invoked, all of the methods that it “points to” are called. This is called “multicasting”.
As we’ve already seen, a Delegate must have the same signature (“shape”) as the method that it is point to. This rule stays exactly the same when a Delegate “points to” multiple methods: the Delegate has the have the same signature as every method that it points to.
In order to enable a Delegate to “point to” multiple methods, the System.Delegate.Combine method must be called. The Combine method creates a new Delegate with an invocation list that concatenates the invocation lists of the delegates that are added, in the same order that they were added.
Here’s an initial example that demonstrates Delegate Multicasting:
Notice that the “d1”, “d2” and “d3” delegates all pointed to methods with the same exact signature (ByVal msg As String): DisplayMessage1, DisplayMessage2, DisplayMessage3.
The Output from the above example looks like this:
A more sophisticated example could expand upon the “Event Handling with Delegates” example above, and change the way the “MyList(Of T)” class sets the “call back” delegate in the “OnItemAdded” method. Also, we can add an “OnItemRemoved” method that can be used by the caller to set a “call back” delegate that can be invoked when an item has been removed from the list.
By enabling multicasting in the “OnItemAdded” delegate event, and in a new “OnItemRemoved” delegate event, we allow the caller to set multiple call back delegates that can be invoked. Then the caller can create different methods to respond when items are added, removed, and when the list changes.
Quite a few things have changed in this example, so if you saw the previous example in the “Event Handling with Delegates” section, make sure you take the time to notice the differences.
Here’s the example:
Ok, so now that we have our multicast enabled “MyList(Of T)” class, let’s take a look at how to use it:
So we learned that Delegates are basically pointers to methods that can be invoked at any time. We also learned that Delegates are strongly-typed, and must have a signature that identically matches the signature of the method that they point to. We also learned that a Delegate intrinsically support multicasting, so that a Delegate can point to multiple methods, not just one method.
If you have a pretty solid understanding of Delegates, then you’re ready to dive into Events, which you’re going to love, because they are designed to lessen the burden of using Delegates in the raw.