Section 8.1. Delegates


8.1. Delegates

An event needs some way to locate the event handler that will act when the event occurs. In some languages, the location of the handler is identified by its memory address, which is stored in a variable called a function pointer. In .NET, the location is stored instead as a delegate.

In pre-.NET application development, function pointers allowed you to call a function generically when you didn't know in advance which function you were going to call. For instance, the Windows API includes a function called EnumFontFamiliesEx that provides a listing of all installed fonts.

     Public Declare Function EnumFontFamiliesEx Lib "gdi32" _        Alias "EnumFontFamiliesExA" ( _        ByVal hdc As Long, _        lpLogFont As LOGFONT, _        ByVal lpEnumFontProc As Long, _        ByVal lParam As Long, _        ByVal dw As Long) _        As Long 

The API works by calling a routine in your program, once for each font. When you use EnumFontFamiliesEx, you pass it the memory address of a callback routine to use for each font; you pass this function pointer through the lpEnumFontProc parameter. The callback routine needs to include a specific parameter list signature, as defined in the API's documentation.

     Public Function EnumFontFamExProc(ByVal lpelfe As Long, _        ByVal lpntme As Long, ByVal FontType As Long, _        ByRef lParam As Long) As Long 

In VB 6, the AddressOf keyword obtains this function pointer, which you then pass to the enumeration API. The problem is that if any little thing goes wrong, your whole program will crash. That's because the function pointer is nothing more than a memory address. It can't guarantee that you put all of the ByVal and ByRef keywords in front of the right parameters, or that you even included parameters at all. In fact, there's nothing to stop you from passing any random number as the function pointer. The API doesn't care, until it crashes.

This is where delegates save the day. A .NET delegate isn't just a function pointer; it's a class that includes everything you need to know to call the destination function correctly. It includes complete information about the parameters and return value, and you won't be able to compile your program until you get it all right.

All delegates derive from the System.Delegate or System.MulticastDelegate classes. The former limits the delegate to a single target event handler, while the latter includes no such limit. Visual Basic uses delegates to bind events to event handlers, and sometimes it seems like a lot of work. Fortunately, Visual Studio links most events and event handlers automatically as you drag-and-drop visual elements.

8.1.1. Using a Delegate to Call a Method

To call a method using a delegate, use the Invoke method of the delegate. To illustrate, consider a class module with a simple method.

     Public Class SimpleClass        Public Sub CallMe(ByVal content As String)           MsgBox(content)        End Sub     End Class 

Delegates are one of the many .NET types. Visual Basic includes a Delegate keyword that defines delegates. Since the goal is to call the CallMe method through a delegate, that delegate must have the same signature as the called method. This small example invokes the delegate from a click on a form, so the delegate's definition appears in the form's class code.

     Delegate Sub MyDelegate(ByVal s As String) 

Finally, in the form's Click event handler (which works via delegates, but don't think about that now), the call to CallMe is made indirectly through a delegate.

     Private Sub Form1_Click(ByVal sender As System.Object, _       ByVal e As System.EventArgs) Handles MyBase.Click        ' ----- Get an instance of the destination class.        Dim destClass As New SimpleClass(  )        Dim theDelegate As MyDelegate        ' ----- Connect the delegate to its target, the CallMe method.        theDelegate = New MyDelegate(AddressOf destClass.CallMe)        ' ----- Make the call.        theDelegate.Invoke("Display this!")     End Sub 

It doesn't seem like much, since the code could have just called destClass.CallMe directly. But it is much, since the delegate provides generic and indirect access to the target routine for those times when the code needs something generic and indirect. MyDelegate can connect to any target routine, as long as the routine shares the same signature. The Framework Class Library takes advantage of this fact by using a common argument signature for all class events.

8.1.2. Using a Delegate as a Generic Callback

A delegate is the perfect solution when a generic callback function is needed. The following example implements a simple sorting routine. Usually, sorting routines are limited to a specific type of content, like integers or strings. That's because the sorting routine has to know how to compare the items. But if you could supply a generic "compare function," the sorting routine could sort anything. Or perhaps you need to sort the same type of data (such as an array of integers), but sort the data based on differing comparison standards. It's this second alternative that appears in the example below.

The first step declares the delegate for the common comparison function. Each compare function takes two integers and returns TRue if they need to be swapped from their current order.

     Public Delegate Function CompareFunction(ByVal valueOne As Integer, _        ByVal valueTwo As Integer) As Boolean 

The two comparison functions take different approaches: one sorts in ascending order, while the other sorts in descending order. As expected, they have the same signature as the delegate.

     Public Function SortAscending(ByVal valueOne As Integer, _       ByVal valueTwo As Integer) As Boolean        If (valueOne > valueTwo) Then Return True Else Return False     End Function     Public Function SortDescending(ByVal valueOne As Integer, _       ByVal valueTwo As Integer) As Boolean        If (valueOne < valueTwo) Then Return True Else Return False     End Function 

Here is the code for the sort routine. It uses the delegate's Invoke method to access the comparison function.

     Public Sub FlexSort(ByVal compareMethod As CompareFunction, _           ByVal dataValues(  ) As Integer)        ' ----- Don't tell anyone, but it's a Bubble Sort.        Dim outer As Integer        Dim inner As Integer        Dim swap As Integer        For outer = 0 To UBound(dataValues)           For inner = outer + 1 To UBound(dataValues)              ' ----- Make the generic delegate call here.              If (compareMethod.Invoke(dataValues(outer), _                    dataValues(inner)) = True) Then                 swap = dataValues(inner)                 dataValues(inner) = dataValues(outer)                 dataValues(outer) = swap              End If           Next inner        Next outer     End Sub 

The sorting code is tested from a button's Click event. Notice that the code does not specifically create a delegate. Since the FlexSort routine's first argument accepts a delegate, and since a delegate is really just a container for a matching function, the delegate gets created anyway by passing the address of a matching function.

     Private Sub Button1_Click(ByVal sender As System.Object, _       ByVal e As System.EventArgs) Handles Button1.Click        ' ----- Click a button, sort some numbers.        Dim counter As Integer        Dim dataValues(  ) As Integer = New Integer(  ) {6, 2, 4, 9}        ' ----- First, try it in ascending order.        FlexSort(AddressOf SortAscending, dataValues)        For counter = 0 To 3            Debug.WriteLine(CStr(dataValues(counter)))        Next counter        Debug.WriteLine("")        ' ----- Next, sort them again in descending order.        FlexSort(AddressOf SortDescending, dataValues)        For counter = 0 To 3            Debug.WriteLine(CStr(dataValues(counter)))        Next counter     End Sub 

The output is:

     2     4     6     9     9     6     4     2 




Visual Basic 2005(c) In a Nutshell
Visual Basic 2005 in a Nutshell (In a Nutshell (OReilly))
ISBN: 059610152X
EAN: 2147483647
Year: 2004
Pages: 712

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net