View Single Post
 
Old 04-11-2004, 10:22 AM
excaliber's Avatar
excaliber excaliber is offline
Senior Contributor

* Expert *
 
Join Date: Nov 2002
Location: Ohio, USA
Posts: 1,828
Default Delegates in VB.Net

Introduction
Here is a quick look at the usage of Delegates. Delegates are extremely powerful, but at first glance don't seem all that useful. Anyhow, a Delegate is a type-safe object oriented function pointer.

What's that mean
Well, lets look at the "function pointer" part first. Every function (or subroutine, or whatever) is really only an address in memory. To execute the function, you just move to the address and begin. A delegate object is just a "mobile" container for the memory address. Now the "type-safe" part. In C language there is a Callback method that provides a function pointer. But because the function pointer contains nothing more than the memory address, it does not have the argument list either. This means you can get some nasty results when trying to use a wrong set of arguments. VB.Net's delegate objects are type-safe though, meaning they will error you provide them the wrong set of arguments.

Simple example
Code:
Class Delegate 'This is the delegate declaration. Each delegate object must 'have a declaration. The Delegate keyword specifies it as a delegate 'declaration Public Delegate Sub delegateDog Private Sub Example() 'Here we create a delegate object by declaring as 'delegateDog (from the delegate declaration above) Dim doDog as delegateDog 'set the object to the AddressOf the Size subroutine doDog = AddressOf Size 'Invoke (or execute) the delegate (which really just executes 'the subroutine) doDog.Invoke() End Sub 'This is the target subroutine that will be used for the delegate Private Sub Size() MsgBox("Big dog") End Sub End Class
When run, a message box will appear with the words "Big Dog". Nifty. We declared a Delegate subrountine (delegateDog), then created an object as one. We set the delegate object to the AddressOf (returns the type-safe memory pointer) of the Size subroutine. Then we invoked the delegate

The cool thing about delegates is their OO properties. You treat them just like any other objects. They can be passed around from class to class, set to other delegates, etc. Very powerful stuff. Having a hard time seeing what's so great? Here are three examples.

The first is going to go without code, as it would require alot of explanation that is unrelated to delegates. In a typical multithreaded environment, any thread that tries to update the parent thread's UI will have trouble. Things start to get flaky when that happens. Instead of directly accessing the UI, or passing a reference to the form, you can pass a delegate. The delegate is set to the UI update function, then passed as an argument to the new thread. The thread can then update the UI whenever it chooses. Why can it do this? Because it's accessing the parent thread's function (and thus in the thread space of the parent), to update the UI (which is perfectly fine).

This is also codeless, because it is more of an explanation. Delegates can be used to give access to any function from any class. Say I have a private function in mySuperClass. I can create a delegate of that function and pass it to myClass. Normally, even if a reference of the class was passed, that function could not be accessed. But because you have a delegate of the fucntion, you can execute it from myClass (despite not having adequate "permission" to do so).

Last example, and this one has code. Ever wonder how Events work? An event just uses Delegates! VB.Net hides some stuff from you to make the interface nicer, but it's the same thing. Let's make a simple app that sets your bank balance (with a slider) and displays your "wealth rating".

First, the bank class:
Code:
Public Class clsBank 'An enum for making our lives easier Public Enum eWealth FilthyRich Rich Modest Poor LivingInBox End Enum 'the delegate declaration, this time it has an argument that may 'be passed as well. The argument is an eWealth Enum Public Delegate Sub BankEventHandler(ByVal WealthRating As eWealth) 'Now we declare the actual delegate object Private doWealthRating as BankEventHandler 'A variable to hold the actual wealth value Private intWealth as Int32 'This function allows the calling class to pass the delegate in initially. 'The delegate object will be passed to us from the calling class so that 'we can manipulate it Public Sub PassDelegate(ByVal doDel as BankEventHandler) doWealthRating = doDel End Sub 'Property that we will be using to show how events are the same as events Public Property WealthRating() As Int32 Get Return intWealth End Get 'If the value set is less than zero, set it to zero Set(ByVal Value As Int32) If Value < 0 Then intWealth = 0 Else 'Otherwise, set the internal wealth variable to the passed value intWealth = Value ' And here set up a select case on the value 'Each different case we invoke the delegate object and pass 'one of the Enum values. This is essentially executing the subroutine 'in the calling class. Select Case intWealth Case is > 100 doWealthRating.Invoke(CartridgeState.FilthyRich) Case 60 To 99 doWealthRating.Invoke(CartridgeState.Rich) Case 30 To 59 doWealthRating.Invoke(CartridgeState.Modest) Case 1 To 29 doWealthRating.Invoke(CartridgeState.Poor) Case is = 0 doWealthRating.Invoke(CartridgeState.LiveInBox) End Select End If End Set End Property End Class
We have alot going on in this code. First we have an Enum to make our lives easier later on. Then we have the declaration of the delegate subroutine, and then the actual object for the delegate. There is a subroutine to pass in the delegate from the calling class, and a property for the wealth rating.

And now the class to use the previous code:
Code:
Public Class Form1 'A new instance of the bank class from the previous code Private oWealth as New clsBank 'On the form load, we want to pass the AddressOf (the type 'safe function pointer) to the new class instance. This is so 'the bank class can manipulate the delegate object. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load oWealth.PassDelegate(AddressOf WealthStatus) End Sub 'Scrollbar event. Here we use the public property of the bank 'to set the value. Private Sub scrBankWealth_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles trInkLevel.Scroll oWealth.WealthRating = scrBankWealth.Value End Sub 'At the same time the scroller is moved, the delegate object in the other 'class will invoke this method (much like an event). It will also pass a 'BankEventHandler argument Private Sub WealthStatus(ByVal Status as clsBank.BankEventHandler) Dim Value as String 'A select case to display the value in a label Select Case Status Case clsBank.BankEventHandler.FilthyRich Value = "FilthyRich" Case clsBank.BankEventHandler.Rich Value = "Rich" Case clsBank.BankEventHandler.Modest Value = "Modest" Case clsBank.BankEventHandler.Poor Value = "Poor" Case clsBank.BankEventHandler.LiveInBox Value = "LiveInBox" End Select lblStatus = State End Sub End Class
Here, we have a new instance of the clsBank Class. On the load of the form, we pass a delegate to the class using AddressOf. Then there is the slider control's subroutine, which updates the class' wealth value. Then there is our delegate subroutine, WealthStatus, which displays the status in a label.

As you slide the slider, the label will change accordingly. Go back and look at the WealthRating property in clsBank. Notice how when it is set, it will invoke the delegate and pass an argument. Looking back at Form1, we see the delegate subroutine accepts the argument and deals with it by a select case and a label output.

Essentially, we just created a custom programmable Event and Event Handler. Pretty cool.

Conclusion
Delegate objects are extremely powerful, allowing you to access functions from anywhere in an object oriented manner. They can also be used to create custom events.
__________________
RandomIRC - Your neighborhood's friendly IRC channel (irc.randomirc.com - #code)

"Perl - The only language that looks the same before and after RSA encryption."

Last edited by excaliber; 04-11-2004 at 10:55 AM. Reason: Added Comments
Reply With Quote