Register | Login 
View Article  

Current Articles | Categories | Search | Syndication

Delegates in VB.Net

By John Spano on Tuesday, February 21, 2006 :: 3268 Views :: 15 Comments :: :: VB.Net

In your Visual Basic.Net journey, you have definitely encountered a well used but little understood phenomenon called a delegate. You use them everyday, but might not know it. In this article, we will take a look at what a delegate is and how it will help you to develop better software.
 
    A delegate can be defined as a type safe function pointer. It encapsulates the memory address of a function in your code. Whenever you create or use an event in code, you are using a delegate. When the event is thrown, the framework examines the delegate behind the event and then calls the function that the delegate points to. As we will see later, delegates can be combined to form groups of functions that can be called together.
 
    Let’s first take a quick look at how to define and invoke a delegate.
First we declare our delegate in our form class:
 
 
Code:
    Private Delegate Sub MyDelSub()
 
 
 
    Then we use the delegate by simply declaring a variable of the delegate and assigning the sub or function to run when called. First the sub to be called:
 
 
Code:
    Private Sub WriteToDebug()
        Debug.WriteLine("Delegate Wrote To Debug Window")
    End Sub
 
 
 
You will notice also that it matches our declaration of MyDelSub; it’s a sub routine with no parameters.
 
And then our test code:
 
 
Code:
        Dim del As MyDelSub
        del = New MyDelSub(AddressOf WriteToDebug)
        del.Invoke()
 
 
 
    When we invoke the delegate, the WriteToDebug sub is run. Visual Basic hides most of the implementation of delegates when you use events, which are based off invoking a delegate. This is the equivalent of the above delegate invoke also.
 
 
Code:
    Private Event MyEvent()'declare it in the class
    'to use it, add a handler and raise the event.
    AddHandler MyEvent, AddressOf WriteToDebug
    RaiseEvent MyEvent()
 
 
 
    If delegates stopped at this point, they would be useless since events are less work and do the same thing. Let’s get into some of the more advanced features of delegates. We will start with multicast delegates.
 
    Multicast delegates allow you to chain together several functions or subs that are all called together when the delegate is invoked. For the current iteration of the framework, you can’t designate the order that the functions are run, only that they are all run, one after another. Let’s look at the code for the multicast delegate.
 
First we add a new sub for our second delegate.
 
Code:
    Private Sub WriteToDebug2()
        Debug.WriteLine("Delegate Wrote To Debug Window 2")
    End Sub
 
 
 
Our declaration of the MySubDelegate stays the same, and here is our new usage code.
 
 
Code:
        Dim del As MyDelSub
        Dim del2 As MyDelSub
        Dim delAll As [Delegate]
 
        del = New MyDelSub(AddressOf WriteToDebug)
        del2 = New MyDelSub(AddressOf WriteToDebug2)
 
        delAll = MulticastDelegate.Combine(del, del2)
        delAll.DynamicInvoke(Nothing)
 
 
 
    As we examine this code, we see three delegate variables; two for our normal delegates that call our subs and one form the combined other two delegates. We set up our normal delegates as always, one points to WriteToDebug, the other to WriteTodebug2. When we combine the two delegates into our third, we utilize the static function Combine, of the MulticastDelegate class. It has two overloads, one that combines two delegates like we used, and one that takes an array of delegates. Next we invoke all the delegates with the combined delegates DynamicInvoke property, passing in Nothing for its parameter. We could have also passed in an array of objects that would be used for parameters to the invoked subs.
 
    If you check out the declaration of the last sample, you see another huge benefit of delegates. Notice that both del and del2 point to different functions, but are of the same type, MyDelSub. This opens up loads of programming potential. It allows you to point a MyDelSub variable to ANY sub that has the same signature as itself. In our case, it’s a simple sub with no parameters. This behavior will let you program more generically. Next we will examine this generic behavior in detail. It works well on both sides of the equation, either invoking a delegate from inside your class or receiving a delegate from outside your class to work upon.
 
    You are already familiar with the concept of having a class invoke a delegate that originates from inside it. Anytime you handle an event from a control you are catching a delegate that has been invoked from inside the control so we won’t go into detail about it. Just remember that when you register a function via handles or addhandler, you are telling a delegate somewhere to make sure it calls your function when it’s invoked. If you have multiple functions the have handles for the same event, you are just using a multicast delegate.
 
    Let’s take a look at some customization with delegates. You can create classes that allow users to input delegates for certain routines. The classes take a list of delegates to call during specific times. This can be used to add different behaviors to your class, such as different sorting routines. This is a more modern and slightly different version of the Visitor design pattern as described in the GOF book. More information about the Visitor design pattern can be found here:
Implementating the Visitor Design Pattern
 
    To start our example, lets take a simple class that represents a dog. It’s very simple, with only one method and a define for our delegate.
 
 
Code:
Public Class Bulldog
 
    Public Delegate Sub BarkMethod()
 
    Public Sub DoBark(ByVal BarksToRun As BarkMethod)
        BarksToRun.DynamicInvoke(Nothing)
    End Sub
End Class
 
 
 
The delegate sub BarkMethod is what we will use to create variables from. The sub DoBark takes an instance of the delegate to run.
 
Now for our test code.
 
Code:
    Dim bk1 As Bulldog.BarkMethod
    Dim bk2 As Bulldog.BarkMethod
    Dim MyDog As Bulldog
 
    MyDog = New Bulldog
    bk1 = New Bulldog.BarkMethod(AddressOf Bark1)
    bk2 = New Bulldog.BarkMethod(AddressOf Bark2)
 
    MyDog.DoBark(bk1)
    MyDog.DoBark(bk2)
 
 
 
We create two delegate variables of the type Bulldog.BarkBethod. We then assign each variable to a sub in our test code, which we see here:
 
 
Code:
    Private Sub Bark1()
        Debug.WriteLine("Woof Woof")
    End Sub
    Private Sub Bark2()
        Debug.WriteLine("Yip Yip")
    End Sub
 
 
 
    The subs simply write to the debug window. As we create and use our dog object, we decide we want to make the dog bark by using Bark1 and then by using Bark2. We simply pass in the correct delegate to the dog object, which calls our external subs to do the work. As you can see, this pattern makes for very easy expansion. If we decide to make the dog talk, then we just add a delegate variable that points to a sub that returns “Hello”.
 
    The next step up would be the need to take multiple delegates for the function instead of the singular bk1 or bk2. To facilitate this you can change the dog class to contain add and remove subs that control an internal delegate of what barks to run. Here is our new dog class.
 
 
Code:
Public Class Bulldog
    Private _Barks As [Delegate]
    Public Delegate Sub BarkMethod()
 
    Public Sub DoBark()
        If Not IsNothing(_Barks) Then 'check to see if there is anthing to Invoke
            _Barks.DynamicInvoke(Nothing)
        End If
    End Sub
    Public Sub AddBark(ByVal Bark As BarkMethod)
        _Barks = MulticastDelegate.Combine(_Barks, Bark)
    End Sub
    Public Sub RemoveBark(ByVal Bark As BarkMethod)
        _Barks = MulticastDelegate.Remove(_Barks, Bark)
    End Sub
End Class
 
 
 
    You can see that we have added a private property, _Barks, to keep track of our delegates to call. Our AddBark and RemoveBark subs use static functions of the MulticastDelegate to add and remove delegate. These functions could also be easily overloaded to take an array of BarkMethods instead of single instances.
 
Here is our new test code.
 
Code:
    Dim bk1 As Bulldog.BarkMethod
    Dim bk2 As Bulldog.BarkMethod
    Dim MyDog As Bulldog
 
    MyDog = New Bulldog
    bk1 = New Bulldog.BarkMethod(AddressOf Bark1)
    bk2 = New Bulldog.BarkMethod(AddressOf Bark2)
 
    MyDog.AddBark(bk1)
    MyDog.AddBark(bk2)
    MyDog.DoBark()
    MyDog.RemoveBark(bk2)
    MyDog.DoBark()
 
 
 
    Our Bark1 and Bark2 subs didn’t change, so I don’t show them. In the test code, we create our normal two delegates and then, using our new methods, add them to our dog class. After we make the dog bark, we remove the bk2 variable from the dog and make him bark again. The output is as follows:
 
Woof Woof
Yip Yip
Woof Woof
 
Delegates can also be used as CallBacks. CallBacks are used in many Windows API calls. You pass in a function pointer to the API call. When the API call gets finished with it’s job, it then “calls back” to the function via the function pointer you passed it. This allows your code to know when the API call is done.
 
    The framework provides an easy way to use a delegate with an API that needs a callback. Here’s how. You can define and pass a delegate as a native function pointer several ways. Here are two. First we declare our delegate and the API call we want to make.
 
 
Code:
    Public Delegate Function MyDelegateCallBack(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
 
    Declare Function EnumWindows Lib "user32" (ByVal x As MyDelegateCallBack, ByVal y As Integer) As Integer
 
 
 
    We then define our function that we want to be called by the EnumWindows API call as it finds windows.
 
 
Code:
    Public Function EnumOutput(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
        Console.WriteLine(hwnd)
        Return True
    End Function
 
 
 
    And last our test code that makes the call to EnumWindow.
 
 
Code:
        Dim del As MyDelegateCallBack
        del = New MyDelegateCallBack(AddressOf EnumOutput)
 
        EnumWindows(del, 0)
 
 
 
    We declare our delegate variable and then pass it to EnumWindows, which calls it every time it finds a new window. The test code could also be written like this:
 
 
Code:
    EnumWindows(AddressOf EnumOutput, 0)
 
 
 
    The shorter form creates a delegate for you and passes it to EnumWindows.
 
    To pass multiple delegates to CallBacks is just as easy. You can’t use the short form of the CallBack delegate as shown above, since you need to combine delegates. In this example the definition of EnumWindows changes along with our test code. Here is the whole example.
 
 
Code:
    Public Delegate Function MyDelegateCallBack(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
 
    Declare Function EnumWindows Lib "user32" (ByVal x As [Delegate], ByVal y As Integer) As Integer
 
    Private Sub btnButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnButton1.Click
        Dim del As MyDelegateCallBack
        Dim del2 As MyDelegateCallBack
        del = New MyDelegateCallBack(AddressOf EnumOutput)
        del2 = New MyDelegateCallBack(AddressOf EnumOutput2)
 
        Dim delAll As [Delegate]
        delall = MulticastDelegate.Combine(del,del2)
 
        EnumWindows(delAll, 0)
 
    End Sub
 
    Public Function EnumOutput(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
        Console.WriteLine(hwnd)
        Return True
    End Function
    Public Function EnumOutput2(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
        Console.WriteLine("Found HWND")
        Return True
    End Function
 
 
 
    You will notice that we use a generic delegate for the first parameter of EnumWindows. This is necessary because we want to pass a multicast delegate to it. We add a new function called EnumOutput2, with the same signature as EnumOutput, and create another delegate that points to it. After creating our multicast delegate, delAll, we use it to pass to EnumWindows. The output shows that both functions get called for each window found. Be careful when doing this. Since EnumWindows takes a generic delegate, you are responsible for passing in functions with the correct signature that it expects.
 
    Now that we know how a CallBack works, we will take a look at how to implement one of our own and call it asynchronously. Like always, we start by defining a delegate.
 
 
Code:
    Public Delegate Sub MyAsyncDelegate()
 
 
 
    Next we will show our test code and delegate functions together and analyze them
 
 
Code:
    Private Sub MyWorker()
        System.Threading.Thread.Sleep(2000)
        Debug.WriteLine("MyWorker Done!")
    End Sub
    Private Sub ImDone(ByVal ar As System.IAsyncResult)
        Debug.WriteLine("AsyncDelegate is done")
    End Sub
    Private Sub btnButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnButton1.Click
        Dim del As MyAsyncDelegate
        del = New MyAsyncDelegate(AddressOf MyWorker)
 
        Dim cb As AsyncCallback = New AsyncCallback(AddressOf ImDone)
 
        Dim oState As Object
 
        Dim ar As IAsyncResult = del.BeginInvoke(cb, oState)
 
        Debug.WriteLine("After Delegate BeginInvoke")
    End Sub
 
 
 
    If you look at our two delegate functions, you will see that the MyWorker function is the target of our normal MyAsyncDelegate. The other function, ImDone, takes a parameter of a System.IAsyncResult. This is the signature you need for the class AsyncCallback.
 
    An AsyncCallback delegate allows you to call a function asynchronously. It takes a parameter of the results. In our test code you will see that we have created a variable named, cb, for our AsyncCallback. This is the delegate that will get called when our function is done.
 
    Next we declare a variable, called ar, of the IAsyncResult type and set it equal to our normal delegate’s BeginInvoke method. This method takes a Callback delegate and an object that represents its state. This call returns immediately. The framework does all the work of making the call to MyWorker on another thread for you.
 
    When MyWorker finishes, we get our asynchronous sub’s output of AsyncDelegate Done. If you examine the whole output, you will see that the asynchronous call return immediately, then the MyWorker sub finishes, and finally the ImDone sub is called.
 
After Delegate BeginInvoke
MyWorker Done!
AsyncDelegate is done
 
    In our example, we have done all the work locally behind a form, but one could easily define a method that took a delegate for a callback. Instead of defining your delegate variable locally, you would use the passed in one as the target of the AsyncCallback object. After setting up, you would call your internal delegate to do the work and return processing to the calling sub. As your delegate worked, it calls the AsyncCallback whenever you decide to notify the client.
 
    Hopefully this article will help you understand delegates and how VB.Net uses them. When applied correctly, they can make your programming very generic and able to handle many different situations. Good luck and happy coding!

Previous Page | Next Page

COMMENTS

hi - my name is george. your article is very nice. i have a problem with vb.net calling a c api functions (which randomly send messages over a period of time say from 10am to 4pm). i wrote tcp/ip sockets to do the communication. it works great. but now the IT team only has C-API functions for me to call to communicate with my VB.NET applicaton. note: the API functions will randomly send me messages; my VB.Net application needs to receive these messages and process them whenever there is a message.

can you please give me some example how to call API functions with events (ie, randomly send messages) embedded in it?

my email is zip10021nyc@yahoo.com

thank you very much!!!

G

posted @ Wednesday, December 13, 2006 7:38 PM by george


Hi George. I'm light on events and callbacks to api, but I "think" the only way to do it is to have the api call itself take a delegate as a funciton pointer. Then the C code can call it when it needs to.

posted @ Wednesday, December 13, 2006 8:43 PM by John spano


thank you!

i am not a professional programmer. but i was able to find code online to send mgs back and forth via tcp/ip sockets in vb.net. now only C API functions are available to me for communication for my vb .net application. the API functions basically send me a text mgs (trades of stocks) whenever there is a trade took place - an event.

can you be more specific? or provide some examples in VB....

thanks

G

posted @ Wednesday, December 13, 2006 8:54 PM by george


sir I am a programmer in vb.net (intermidiate level). I wanted to study the delegates therefore i read ur articcal. I realy liked this artical because of neat examples.
what i understood from this article is that delegates are used to communicate between different classes. Is it right?
please let me know. Also i would like to have more commercial examples. So i request u to please forward these exaples to me.
Thank U very much

posted @ Friday, December 15, 2006 6:11 AM by Sunil Patil


George, where did you get the api from? If you can't modify them, and they don't already take a callback pointer, you won't be able to get them to fire an event.

posted @ Friday, December 15, 2006 5:56 PM by John Spano


Sunil, they can be used to communicate between classes. They are also used for callbacks and simple events. IE a button click event is really just a delegate under the covers.

posted @ Friday, December 15, 2006 5:57 PM by John spano


ok - this is a real application about stock trading. the API are written by the IT group at some bank. i need to get al the real time market data (such as IBM currnet price, traded side, volumn, etc....the most current traded information). these real time market data are coming from API functions. my vb.net application needs to call these API functions and and automatically updating the variables in my applications whenever there are tradings in the market. i can do the communications via IP sockets. but now the bank told me they only provide API (which i believe was written based in IP sockets)....so i need some real examples to call C-API from vb.net.....
thank you for your help! george in nyc

posted @ Friday, December 15, 2006 8:54 PM by george


why this callback function is "sequential"?!?!?!?

the command lines in the main sub will NOT continue until the callback function completes. why?!?! can i let the main sub continue...while the callback function keeps updating some public variable(s)???


please see the code below....

george


Imports System
Imports System.Runtime.InteropServices
Module CallbackFunctionTesting
Public Delegate Function CallBack( _
ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean

'Public Class EnumReportApp

Declare Function EnumWindows Lib "user32" ( _
ByVal x As CallBack, ByVal y As Integer) As Integer

Public xCount As Integer = 0
Public xLen As Integer = 0

Public Sub Main() 'Shared Sub Main()
'EnumWindows(AddressOf EnumReportApp.Report, 0)
EnumWindows(AddressOf Report, 0)

Dim i As Integer
For i = 1 To 200
Console.WriteLine("xLen = " & xLen)
System.Threading.Thread.Sleep(1000)
Next
MsgBox("hold on!!")
End Sub 'Main

Public Function Report(ByVal hwnd As Integer, ByVal lParam As Integer) _
As Boolean
Console.Write("Window handle is ")
Console.Write(hwnd)
Console.Write("; this is lParam = " & lParam)
xLen = (hwnd).ToString.Length
Console.WriteLine("; the lenth of handle is " & xLen)

System.Threading.Thread.Sleep(1000)

xCount = xCount + 1
If xCount > 16 Then
'MsgBox("xCount is reached here!!!")
xCount = 0
End If
Return True
End Function 'Report
'End Class 'EnumReportApp

End Module

posted @ Sunday, December 17, 2006 3:16 PM by george


your enum declaration is off some. see this article
http://www.informit.com/guides/content.asp?g=dotnet&seqNum=111&rl=1

posted @ Sunday, December 17, 2006 4:34 PM by John Spano


what do you mean by "off some"?????
can you please be specific?

thanks

george

posted @ Sunday, December 17, 2006 6:30 PM by george


please see the article I posted in the last comment. It goes in to much more detail on how ot use enum windows. The first param you have is defined as a callback, but it should be an integer.

posted @ Sunday, December 17, 2006 6:36 PM by John Spano


my code is working......but it is SEQUENTIAL...why? .i need it to work in parallel.....how?

tell me why and how. thank you

G

posted @ Sunday, December 17, 2006 6:57 PM by g


the delegate is in a different thread. correct?
if it is true, then the delegate function should run indepenent of the calling main sub. so, if i put something sleep in delegate function, then the immediate command lines in the main sub after calling delegates should be executed before the delegate due to the sleep in the delegate function. correct? but the reality does not work like that. it always executes the delegate function first, after the delegate function completes, then it executes the command lines after the calling delegate command line.... why??

can someone help me to explain?!?!?

thanks

george

posted @ Friday, December 22, 2006 3:58 PM by george


just because it's a delegate, doesn't give you another thread. It's just a function pointer, what you do with it is up to you. in the case of enum windows, it doesn't run in a separate thread. enum windoes uses it to call back each window it finds. it won't return from the call until it gets done though. It's up to you to multithread the call if you want it to behave that way.

posted @ Friday, December 22, 2006 6:08 PM by John Spano


thank you!!!!

i need some delegates which will run in separate threads and will pass back to the main sub or update some public variables in the main sub upon some events took place in the delegate functions.

can you please show me some example(s)???

thank you!!

george

posted @ Friday, December 22, 2006 6:15 PM by george


Click here to post a comment

Copyright (c) 2008 GSP Developers
Walling Info Systems | Terms Of Use | Privacy Statement