Understanding Delegates using Visual Basic.NET 2005
This article gives you a solid understanding of delegates using Visual Basic.NET 2005. You will need a good foundation in object-oriented programming and Visual Basic.NET to understand this article.
The entire source code for this article is available in the form of a downloadable zip file. The solution was developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Enterprise Edition. I didn't really test it in any other environment. I request that you post in the discussion area if you have any problems in execution.
A simple introduction to Delegates using Visual Basic 2005
A delegate allows us to encapsulate a reference to a method inside an object -- a delegate object, to be precise. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked.
Before trying to understand the above, let us work with a simple example. The following is a sample class:
Public Class Sample01
Private _x As Integer
Private _y As Integer
Public Sub New()
End Sub
Public Sub New(ByVal a As Integer, ByVal b As Integer)
_x = a
_y = b
End Sub
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Public Property Y() As Integer
Get
Return _y
End Get
Set(ByVal value As Integer)
_y = value
End Set
End Property
Public Sub Add()
MessageBox.Show("Sum = " & (Me.X + Me.Y))
End Sub
Public Sub Multiply()
MessageBox.Show("Product = " & (Me.X * Me.Y))
End Sub
End Class
The above class has two private fields ("_x" and "_y") which are only accessible within the class, and not outside the class. Further, it has two public properties and two public methods, "Add" and "Multiply." Note that public members are accessible even outside the class.
To test the above class, add a new form with two buttons and a label. Modify your code to match the following:
'without using delegates
Public Class Form1
Dim obj As New Sample01(10, 20)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
obj.Add()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
obj.Multiply()
End Sub
End Class
There is nothing new in the above form. We are simply instantiating an object based on the "Sample01" class and calling its methods. We will do the same in the next section, but with "Delegates."
In the previous section, a class named "Sample01" was introduced. The following is a simple "delegate" way of coding:
'sample of using delegate
Public Class Form2
Delegate Sub Calculate()
Dim obj As New Sample01(10, 20)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim delg As New Calculate(AddressOf obj.Add)
delg.Invoke()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim delg As New Calculate(AddressOf obj.Multiply)
delg.Invoke()
End Sub
End Class
Let us try to understand it step by step. First of all we have the following:
Delegate Sub Calculate()
The line says that "Calculate" is a delegate. Even though its signature looks like a method, it is actually a class. Consider "Calculate" to be a named class of yours having its own functionality to invoke "methods of other objects dynamically."
Further proceeding we have the following:
Dim obj As New Sample01(10, 20)
It is simply an instantiation. Further on we have the following:
Dim delg As New Calculate(AddressOf obj.Add)
As previously described, "Calculate" is a class. And now, "delg" is an instance of the class "Calculate," which is "authorized" to access and execute the method named "obj.Add()".
The "obj.Add()" method gets executed when the following statement is invoked:
delg.Invoke()
Finally, a delegate is simply an object which can execute methods of other objects dynamically at run time.
To make all of this simpler, the above can also be written as follows:
'without creating instances of a delegate and without using invoke
Public Class Form3
Delegate Sub Calculate()
Dim obj As New Sample01(10, 20)
Dim deleg As Calculate
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
deleg = AddressOf obj.Add
deleg()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
In my previous example, you were introduced to simple delegates. Now, I shall extend the same with parameters.
To help us understand "delegates with parameters," I added a new class as follows:
Public Class Sample02
Private _x As Integer
Private _y As Integer
Public Sub New()
End Sub
Public Sub New(ByVal a As Integer, ByVal b As Integer)
_x = a
_y = b
End Sub
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Public Property Y() As Integer
Get
Return _y
End Get
Set(ByVal value As Integer)
_y = value
End Set
End Property
Public Sub Increment(ByVal IncrementValue As Integer)
_x += IncrementValue
_y += IncrementValue
End Sub
Public Sub Add()
MessageBox.Show("Sum = " & (Me.X + Me.Y))
End Sub
Public Sub Multiply()
MessageBox.Show("Product = " & (Me.X * Me.Y))
End Sub
End Class
You can observe that a new method, "Increment" (which accepts a parameter), is added. Let us try to access it using delegate. The following is the code:
'delegate with parameters
Public Class Form4
Delegate Sub Calculate()
Delegate Sub IncreaseValues(ByVal value As Integer)
Dim delegCalc As Calculate
Dim delegIncrease As IncreaseValues
Dim obj As New Sample02(10, 20)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
delegCalc = AddressOf obj.Add
delegIncrease = AddressOf obj.Increment
delegIncrease(100)
delegCalc()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
delegCalc = AddressOf obj.Multiply
delegIncrease = AddressOf obj.Increment
delegIncrease(100)
delegCalc()
End Sub
End Class
Let us try to understand this step by step. Consider the following first:
Delegate Sub IncreaseValues(ByVal value As Integer)
"IncreaseValues" is a delegate type of class now. However, it only accepts methods (which can be executed later) with just one parameter of type integer.
Dim obj As New Sample02(10, 20)
delegIncrease = AddressOf obj.Increment
As the "Increment" method accepts a parameter of type integer, it can be assigned to the delegate object we declared earlier.
Finally, the method gets executed with a value of 100 using the statement:
delegIncrease(100)
A delegate declaration can have multiple parameters, not only one. And further, those parameters could be of any type, from primitive data types to reference types!
In all of the previous examples, I concentrated only on methods which do not return any values. Now, let us concentrate on delegates which can be used to call methods returning values (say, functions in a class).
Let us add one more class as follows:
Public Class Sample03
Private _x As Integer
Private _y As Integer
Public Sub New()
End Sub
Public Sub New(ByVal a As Integer, ByVal b As Integer)
_x = a
_y = b
End Sub
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Public Property Y() As Integer
Get
Return _y
End Get
Set(ByVal value As Integer)
_y = value
End Set
End Property
Public Function GetSum() As Integer
Return (Me.X + Me.Y)
End Function
Public Function GetProduct() As Integer
Return (Me.X * Me.Y)
End Function
End Class
The above class contains two methods, "GetSum()" and "GetProduct()," which return values of type integer. To access those methods using delegates, you can code as follows:
'delegates to functions
Public Class Form5
Delegate Function Calculate() As Integer
Dim delegCalc As Calculate
Dim obj As New Sample03(10, 20)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
delegCalc = AddressOf obj.GetSum
MessageBox.Show("Sum = " & delegCalc())
'MessageBox.Show("Sum = " & delegCalc.Invoke())
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
"Callback" is one of the most important features of delegates. Let us start with an example. To make things easier to understand, I added a new class as follows:
Public Class Sample04
Delegate Sub FactorFound(ByVal FactorValue As Integer)
Private _x As Integer
Public Sub New()
End Sub
Public Sub New(ByVal a As Integer)
_x = a
End Sub
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Public Sub FindFactors(ByVal delgFoundFactor As FactorFound)
For i As Integer = 1 To _x
If _x Mod i = 0 Then
delgFoundFactor(i)
End If
Next
End Sub
End Class
The most important method from the above class is the following:
Public Sub FindFactors(ByVal delgFoundFactor As FactorFound)
For i As Integer = 1 To _x
If _x Mod i = 0 Then
delgFoundFactor(i)
End If
Next
End Sub
The method accepts a parameter of type "delegate" which is declared at module level as follows:
Delegate Sub FactorFound(ByVal FactorValue As Integer)
That means the calling program can execute the "FindFactors" method by passing the address of another method. The address, which is passed to "FindFactors," can be invoked within the same "FindFactors" method.
In simple words, the calling program executes "FindFactors" by giving permission to the "FindFactors" method to execute (internally inside "FindFactors") another method (address) passed to it.
The following sample can be considered a caller for this demonstration:
'implementing callback methods using delegates
Public Class Form6
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.ListBox1.Items.Clear()
Dim obj As New Sample04(Me.TextBox1.Text)
obj.FindFactors(AddressOf FactorAvailable)
End Sub
Private Sub FactorAvailable(ByVal FactorValue As Integer)
Me.ListBox1.Items.Add(FactorValue)
End Sub
End Class
From the above, you can observe that "FactorAvailable" is passed to the "FindFactors" method. The "FactorAvailable" method gets executed with the following statement in the "FindFactors" method:
A delegate (object) which simultaneously executes more than one method dynamically can be called as a multicast delegate.
In all of my previous examples, I attached a delegate to one and only one method every time. Now, we are going to assign more than one method to the same delegate. In other words, the moment "invoke()" gets executed, all the methods (addresses of methods) attached to the delegate get executed.
Let us see an example of this:
'multi-cast delegate
Public Class Form7
Delegate Sub Calculate()
Dim obj As New Sample01(10, 20)
Dim deleg As Calculate
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
deleg = AddressOf obj.Add
deleg = System.Delegate.Combine(deleg, New Calculate(AddressOf obj.Multiply))
'deleg.Invoke()
deleg()
End Sub
End Class
From the above, the most important lines are the following:
deleg = AddressOf obj.Add
deleg = System.Delegate.Combine(deleg, New Calculate(AddressOf obj.Multiply))
The first line says that the "Add" method must be assigned to the "deleg" object. The second line says that another method, "Multiply," must be assigned to the "deleg" object, to keep existing.
Finally, all the methods in the delegate get executed using the following line:
deleg()
I hope you enjoyed the article and any suggestions, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com