Delegates In .Net
A delegate can be defined as a type safe function pointer.
You use delegates to call the methods of other objects.
They are object-oriented function pointers since they allow a function to be invoked indirectly
by using a reference to the function.
It wraps up the memory address of a function in your code.
The system makes use of delegate whenever you create or use an event in code.
When an event is called,
the framework examines the delegate behind the event
and then calls the function that the delegate points to.
However, unlike function pointers,
the delegates in .NET are reference types,
based on the class System.Delegate.
In addition, delegates in .net can reference both shared and instance methods.
Using Delegates
You can declare a Delegate object and then instantiate it.
You can then call the delegate.
The following example shows how a Delegate object is declared, instantiated and called.
Public Class
myownclass
Inherits System.Windows.Forms.Form
'decalring a delegate object
Delegate Sub
mydelegate()
Dim delegateobject As
mydelegate
Public Sub
msgeven()
MsgBox("You have entered an even number")
End Sub
Public Sub
msgodd()
MsgBox("You have entered an odd number")
End Sub
Public Sub
CallsOneSub(ByVal number
As Integer)
Dim result As
Integer
result = number Mod 2
If result = 0 Then
delegateobject = New mydelegate(AddressOf
msgeven)
Else
delegateobject = New mydelegate(AddressOf
msgodd)
End If
delegateobject()
End Sub
In the above code,
a Delegate type called mydelegate is declared.
An object of Delegate type is declared as delegateobject.
Two procedures msgeven(), msgodd() are written.
In the procedure CallsOneSub(), depending on the value of the argument,
delegateobject is instantiated either for msgeven() or msgodd().
The call to delegateobject() will result in the call to the appropriate method.
Why do we have delegates
Delegates are there for the same reason as we had interfaces in VB6.
They ensure that whatever funtion is reference,
it will have the signature we require,
meaning that we can know at design time what parameters we can pass,
although we don't know to what function we will be passing them eventually.
Take multithreading for instance,
we need to pass a delegate for ThreadStart,
which is a procedure that doesn't take any parameters.
Because we need to pass a delegate,
it needs to be a procedure with no parameters,
the name doesn't matter.
Passing any other procedure (with parameters) will result in error.
Delegates & AddressOf Operator
Delegates are used when there is a need for
an intermediary between a calling procedure and the procedure being called.
The need for an intermediary arises in situations when an object that raises
an event should be able to call diffrenet handlers under different circumstances.
As the object that raises events cannot know before hand which event handler is
handling a specific event, there is a need for an intermediary that can dynamically
associate event handlers with events.
In .NET, a delegate is used as the intermediary when the AddHandler attaement is used.
At runtime, the delegate automatically forwards event calls to the appropriate event handlers.
The AddressOf operator implicitly creates an instance of a delegate.
Assume that you create a button called cmdsave and write two event handlers
for the click event of the button, cmdsave_click1 and cmd_click2.
You can add radio buttons to the form that has the button and decide on the event handler
to be called depending on the button selected.
To do so, you will write the following code in the form class:
Private
Sub cmdsave_Click1(ByVal
sender As System.Object,
ByVal e As
System.EventArgs)
MsgBox("Message from Click1")
End Sub
Private Sub
cmdsave_Click2(ByVal sender
As System.Object, ByVal
e As System.EventArgs)
MsgBox("Message from Click2")
End Sub
Private Sub
RadioButton1_CheckedChanged(ByVal sender
As System.Object, ByVal
e As System.EventArgs)
Handles RadioButton1.CheckedChanged
RemHandler() 'calling the remove handler function
AddHandler cmdsave.Click,
AddressOf Me.cmdsave_Click1
End Sub
Private Sub
RadioButton2_CheckedChanged(ByVal sender
As System.Object, ByVal
e As System.EventArgs)
Handles RadioButton2.CheckedChanged
RemHandler() 'calling the remove handler function
AddHandler cmdsave.Click,
AddressOf Me.cmdsave_Click2
End Sub
Public Sub
RemHandler()
RemoveHandler cmdsave.Click,
AddressOf Me.cmdsave_Click1
RemoveHandler cmdsave.Click,
AddressOf Me.cmdsave_Click2
End Sub
In the above code, two event handlers cmdsave_click1 and cmd_click2,
are defined for the click event of a button named cmdsave.
Two radio buttons named RadioButton1 and RadioButton2 are added.
On selecting RadioButton1,
AddHandler is used to associate cmdsave_click1 with ths click event of the button.
On selecting RadioButton2, AddHandler is used to associate cmdsave_click2 with the click event
of the button. When you click the button, a message box is displayed depending
on the radio button selected.
In the above code there is no mention of a delegate object because a delegate object
is returned implicitly.
In addition, assuming you select RadioButton1 first and then select RadioButton2,
then both the event handlers will be called.
This happens because both the event handlers get added to the list of event handlers
for the click event of the button.
You can remove event handlers dynamically by using the RemoveHandler keyword.
How do Delegates actually work internally
The implementation of Delegates is not really complicated
because CLR and VB.NET compiler does a lot of things behind the scenes.
Public delegate mydelegate(argument1)
When the .NET compiler sees the above line,
it creates a public class myDelegate because it is declared as Public Delegate.
It would have created a private class if the delegate was declared private.
This class will inherit from System.MultiCastDelegate.
All properties and methods of System.MulticastDelegate class will be inherited
to the mydelegate Delegate Class
__[CLS] mydelegate
| | |
.class public auto ansi sealed
| | |
extends [mscorlib]System.MulticastDelegate
| |
|___[MET] method .ctor : void(object,native int)
| | |___[MET]
method BeginInvoke : class [mscorlib]System.IAsyncResult(string,class
[mscorlib]System.AsyncCallback,object)
| | |___[MET]
method EndInvoke : void(string,class [mscorlib]System.IAsyncResult)
| |
|___[MET] method Invoke : void(string)
| |
The below line from ILDASM calls the invoke method
of the delegate class:
|___[MET] method Invoke : void(string)
The compiler generates code to call the delegate object
invoke method when it sees
objDelegate.Invoke(PhoneNo)
Multicasting
There can be cases where you would want to call more than one method
through one delegate. This is known as multicasting. To do this we make use of Delegate.
Combine shared method. The Combine method takes an array of delegates as a parameter
and returns a new delegate. This new delegate represents the combination of all the
delegates in that array.
To see how this works, in the following code,
I'll create a simplistic class that declares a delegate:
Public
Class MyDelegate
' the delegate declaration
Public Delegate
Sub StrDelegate(ByVal
s As String)
End
Class
Public
Class MynewClass
Public Shared
Sub WriteStr(ByVal
str As String)
Console.WriteLine(".......Writing string {0}........", Str)
End Sub
Public Shared
Sub LogStr(ByVal
s As String)
Console.WriteLine("........Logging string {0}......", Str)
End Sub
Public Shared
Sub TransmitStr(ByVal
str As String)
Console.WriteLine("........Transmitting string {0}........", Str)
End Sub
End
Class
Instantiate three StrDelegate objects in the Run method of
the MynewClass as follows:
Dim Writer, Logger, Transmitter
As MyClassWithDelegate.StringDelegate
The instantiate these delegates by passing in the address
of the methods that you have to wrap as follows:
myWriter =
New MynewDelegate.StrDelegate( _AddressOf
MynewClass.WriteStr)
myLogger =
New MynewDelegate.StrDelegate( _AddressOf
MynewClass.LogStr)
myTransmitter
= New MynewDelegate.StrDelegate(_AddressOf
MynewClass.TransmitStr)
Now we will instantiate a multicast delegate.
This is what will be used to combine the other three delegates:
Dim
myMulticastDelegate As MyDelegate.StrDelegate
Make an array of the two delegates:
Dim
myarr() As MyDelegate.StrDelegate = {myWriter,
myLogger}
Now use this array to instantiate the multicast delegate:
mymulticastDelegate = _ DirectCast(System.Delegate.Combine(myarr),
_
MyDelegate.StrDelegate)
You can add the third delegate as follows:
myMulticastDelegate = _
DirectCast(System.Delegate.Combine(myMulticastDelegate,
myTransmitter), _
MyDelegate.StrDelegate)
You can remove a delegate form the collection as follows:
myMulticastDelegate = _
DirectCast(System.Delegate.Remove(myMulticastDelegate,
myLogger), _
MyDelegate.StrDelegate)
Difference Between Delegates and Interfaces
You use delegates when you think of a .NET code that takes callback parameters,
which look a lot like strongly typed method pointers in C++.
You may not think that you can implement a callback parameter as an interface.
Interfaces and delegates have a common key property of allowing you to call a
method with the right prototype without knowing which object implements the method,
which instance is bound to the call,
or the name of the method you're calling.
1. Interface calls are faster than delegate calls.
An interface reference is a reference to an instance of an object which implements the interface.
An interface call is not that different from an ordinary virtual call to a method.
A delegate reference, on the other hand, is a reference to a list of method pointers.
While invoking a delegate looks like you're making an indirect call through a method pointer,
it's actually a subroutine call that walks the list of method pointers.
The overhead involved in making the call and walking the list means that delegate invocation
can be two or three times slower than calling a method through an interface reference.
2. Interfaces are also a bit more general than are delegates.
A single interface reference gives you access to all the methods of the interface.
You can also check if the interface is implemented by this object type,
or if the object also implements that other interface.
If it does, you can cast the interface reference to an instance reference,
or to a reference to another interface.
Conversely, you can not go from a delegate to the instances it will call or to any other methods
those instances may support.
However, don't conclude from this that you should always implement callbacks
via interfaces, not delegates.
One key difference between delegates and interfaces is that you can create a delegate to any method
with the right prototype.
|