Section 7.1. Delegates

   

7.1 Delegates

In a never-ending effort to deny VB programmers the right to use pointers, Microsoft has implemented a feature called delegates that, according to the documentation, provide a safe alternative to function pointers.

As you may know, a pointer variable (or pointer ) is simply a variable whose value is interpreted by the compiler as a memory address. The address to which the pointer points is the target of the pointer, and we say that the pointer variable points to that target address. If the target address is a variable of data type Integer, for example, then we say that the pointer is of type Integer or is an Integer pointer. Thus, the type of a pointer is the type of the target variable. (We have seen that, as reference types, variables of type Object and String are both pointers; i.e., their values point to the address of the data in memory.)

A pointer can also point to a function, that is, contain the address of a function. Even though a function is not a variable, it does have a physical location in memory and so can be the target of a pointer. (Actually, it's reasonable to think of a function as a type of variable, but that is another story.) In this case, we have a function pointer .

Function pointers are very useful in certain situations for calling or specifying functions. This is commonly done in the C++ programming language, where function pointers are supported directly.

One area in which function pointers are used is in the context of callback functions . To illustrate , if we want to enumerate all of the fonts on a given system, the Windows API provides a function called EnumFontFamiliesEx , defined as follows :

 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 third parameter requires the address of a function we must declare, called a callback function. The reason for this term is that Windows will call our callback function for each font in the system, passing information about the font in the parameters of the function. According to the documentation, the callback function must have a particular form:

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

The point here is that to use EnumFontFamiliesEx , we need to pass the address of a function as one of the parameters. As you may know, this is done in VB using the AddressOf operator. In earlier versions of VB, this operator is described as follows:

A unary operator that causes the address of the procedure it precedes to be passed to an API procedure that expects a function pointer at that position in the argument list.

Put another way, the AddressOf operator is implemented in VB 6 for the express purpose of passing function addresses to API functions.

In VB.NET, the AddressOf operator returns a delegate, which is, as the documentation states:

A unary operator that creates a procedure delegate instance that references the specific procedure.

So let us discuss delegates. We begin with a rather unhelpful definition: a delegate is an object of a class derived from either the Delegate class or the MulticastDelegate class. These two classes are abstract, so no objects of these classes can be created. Nevertheless, other classes can be derived from these classes, and objects can be created from these derived classes.

In VB.NET, delegates can be used to call methods of objects or to supply callback functions. In addition, VB.NET uses delegates to bind event handlers to events. Fortunately, VB.NET also supplies tools (such as the AddHandler method) to automate this process, so we don't need to use delegates directly for this purpose.

A delegate object inherits a number of properties and methods from the Delegate or MulticastDelegate class. In particular, a delegate object has:

  • A Target property that references the object or objects whose method or methods are to be called

  • A Method property that returns a MethodInfo object that describes the method or methods associated with the delegate

  • An Invoke method that invokes the target method or methods

By now you have probably guessed that there are two delegate classes because delegates derived from the Delegate class can only call a single method, whereas delegates derived from MulticastDelegate can call multiple methods.

7.1.1 Using a Delegate to Call a Method

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

 Public Class Class1     Public Sub AMethod(ByVal s As String)         Msgbox(s)     End Sub End Class 

Now, in a module with a Windows Form (referred to as a form module in earlier versions of VB), we declare a (single cast) delegate with the same parameters as the target method we wish to call:

 Delegate Sub AMethodDelegate(ByVal s As String) 

The following code then uses the delegate to call the AMethod of Class1:

 Protected Sub Form1_Click(ByVal sender As Object, _                           ByVal e As System.EventArgs) _                           Handles MyBase.Click     ' Object of type Class1 _     Dim obj As New Class1(  )          ' Declare a new delegate     Dim delg As ADelegate    ' Define the delegate, passing the address     ' of the object's method     delg = New ADelegate(AddressOf obj.AMethod)     ' Now call the method using the delegate's Invoke method      delg.Invoke("test")      End Sub 

Note that the documentation describes the delegate constructor as taking two parameters, as in:

 delg = New ADelegate(TargetObject, PointerToMethodOfObject) 

However, Visual Basic is not capable of handling the second parameter, so VB supports the special syntax:

 delg = New ADelegate(AddressOf obj.AMethod) 

We point this out only to warn you about the documentation on the delegate class constructor.

7.1.2 Using a Delegate as a Function Pointer

The following example illustrates the use of a delegate in the context of a callback function. In this example, we want to create a generic sort function for sorting integer arrays. The function uses the bubble sort algorithm for sorting, but it's generic in the sense that one of its parameters is a compare function that is used to do the comparison of adjacent integers. Thus, by varying the external comparison function, we can change the type of sorting ( ascending , descending, or some other method) without changing the main sort function. The compare function is a callback function, since it is a function we supply that is called by the main sort function. (In this case, the callback function is not supplying us with information, as in the font enumeration case described earlier. Instead, it is called to help the sort function do its sorting.)

First, we declare a delegate. As part of the declaration of a delegate, we must specify the signature of the method that is associated with the delegate, which, in our case, is the compare function. Since the compare function should take two (adjacent) integers and return True if and only if we need to swap the integers in the bubble sort algorithm, we declare the delegate as follows:

 ' Returns True if need to swap Delegate Function CompareFunc(ByVal x As Integer, _                               ByVal y As Integer) _                               As Boolean 

Here are two alternative target methods for the delegate ” one for an ascending sort and one for a descending sort:

 Function SortAscending(ByVal x As Integer, ByVal y As Integer) As Boolean     If y < x Then         SortAscending = True     End If End Function Function SortDescending(ByVal x As Integer, _                         ByVal y As Integer) As Boolean     If y > x Then         SortDescending = True     End If End Function 

Now we can define the sort routine. Note the call to the Invoke method of the delegate:

 Sub BubbleSort(ByVal CompareMethod As CompareFunc, _                ByVal IntArray(  ) As Integer)     Dim i, j, temp As Integer     For i = 0 To Ubound(IntArray)         For j = i + 1 To Ubound(IntArray)             If CompareMethod.Invoke(IntArray(i), IntArray(j)) Then                 Temp = IntArray(j)                 IntArray(j) = IntArray(i)                 IntArray(i) = Temp             End If         Next j     Next i End Sub 

Here is some code to exercise this example:

 Protected Sub Button1_Click(ByVal sender As Object, _                             ByVal e As System.EventArgs)     Dim i As Integer     Dim iArray() As Integer = New Integer(  ) {6, 2, 4, 9}     BubbleSort(AddressOf SortAscending, iArray)     For i = 0 To 3         Debug.WriteLine(CStr(iArray(i)))     Next     Debug.WriteLine     BubbleSort(AddressOf SortDescending, iArray)     For i = 0 To 3         Debug.WriteLine(CStr(iArray(i)))     Next   End Sub 

Alternatively, we can define delegate variables instead of using the Addressof operator directly:

 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click         Dim i As Integer         ' Instances of the delegate type CompareFunc         Dim dlgAsc As New CompareFunc(AddressOf SortAscending)         Dim dlgDesc As New CompareFunc(AddressOf SortDescending)         Dim iArray() As Integer = New Integer(  ) {6, 2, 4, 9}         BubbleSort(dlgAsc, iArray)         For i = 0 To 3             Debug.WriteLine(CStr(iArray(i)))         Next         Debug.WriteLine("")         BubbleSort(dlgDesc, iArray)         For i = 0 To 3             Debug.WriteLine(CStr(iArray(i)))         Next     End Sub 

The output is, as you would expect:

 2 4 6 9 9 6 4 2 
   


VB.Net Language in a Nutshell
VB.NET Language in a Nutshell
ISBN: B00006L54Q
EAN: N/A
Year: 2002
Pages: 503

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