Delegates

 <  Day Day Up  >  

Events are built on top of delegates , which are types that represent references to methods . A delegate is declared just like a subroutine or a function.

 Delegate Sub SubroutineDelegate(ByVal x As Integer, ByVal y As Integer) Delegate Function FunctionDelegate() As Integer 

A variable whose type is a delegate type can then contain a reference to any method in any type that has the exact same set of parameters and the same return type. For example, the following code creates a new instance of a SubroutineDelegate that refers to the method Button.Move .

 Class Button   Public Sub Move(ByVal x As Integer, ByVal y As Integer)     ...   End Sub End Class Module Test   Sub Main()     Dim s As SubroutineDelegate     Dim b As Button = New Button()     s = New SubroutineDelegate(AddressOf b.Move)   End Sub End Module 

Once a delegate has been constructed , it can be passed around like any normal value. It can also be invoked just as a subroutine or function is invoked. When the delegate is invoked, it calls the method that the delegate refers to. In the following example, the delegate s is invoked and calls the method Button.Move .

 Module Test   Sub Main()     Dim s As SubroutineDelegate     Dim b As Button = New Button()     s = New SubroutineDelegate(AddressOf b.Move)     s(10, 20)   End Sub End Module 

The power of delegates is that they do not represent a reference to a particular method in a particular class, but instead represent a reference to a method with some general signature. Any method that matches the signature of the delegate can be referred to by that delegate. For example, the following function modifies the elements of an Integer array based on a custom modification function that's passed in as a delegate.

 Module Test   ' Delegate performs some action on the value.   Delegate Function ModifyDelegate(ByVal Value As Integer) As Integer   Sub ModifyArray(ByVal a() As Integer, ByVal Modify As ModifyDelegate)     For Index As Integer = 0 To a.Length() - 1       a(Index) = Modify(a(Index))     Next Index   End Sub   ' Adds one to its argument   Function AddOne(ByVal i As Integer) As Integer     Return i + 1   End Function   ' Divides its argument by two   Function DivideByTwo(ByVal i As Integer) As Integer     Return i \ 2   End Function   Sub Main()     Dim a(9) As Integer     ' Add one to each element of the array     ModifyArray(a, AddressOf AddOne)     ' Divide each element of the array by two     ModifyArray(a, AddressOf DivideByTwo)   End Sub End Module 

Because a delegate is a reference type, it must be created before it can be used. All delegate types have the same kind of constructor, which takes a single parameter. The argument to the constructor must be an AddressOf expression. The AddressOf operator takes either a shared method or an instance method qualified with an instance and produces a reference to that particular method. This reference is then stored in the delegate. For example:

 Class Class1   Public Sub S1(ByVal x As Integer, ByVal y As Integer)     MsgBox(x)   End Sub   Public Shared Sub S2(ByVal x As Integer, ByVal y As Integer)     MsgBox(x)   End Sub   Public Function F() As Integer     Return 10   End Function End Class Module Test   Sub Main()     Dim s1, s2 As SubroutineDelegate     Dim f As FunctionDelegate     Dim t As Class1 = New Class1()     s1 = New SubroutineDelegate(AddressOf t.S1)     s2 = New SubroutineDelegate(AddressOf Class1.S2)     f = New FunctionDelegate(AddressOf t.F)   End Sub End Module 

Notice in the example that the shared method S2 was qualified with the class name ”in the case of shared methods, no instance is required to construct a delegate. If, however, the code had tried to say AddressOf Class1.S1 , a compile-time error would have occurred because an instance is required for instance methods.

As long as the type of delegate can reasonably be inferred from the surrounding context, an AddressOf expression can be used in place of the full delegate construction syntax. So the following code:

 s1 = New SubroutineDelegate(AddressOf t.S1) s2 = New SubroutineDelegate(AddressOf Class1.S2) f = New FunctionDelegate(AddressOf t.F) 

could have been written as follows :

 s1 = AddressOf t.S1 s2 = AddressOf Class1.S2 f = AddressOf t.F 

This is an example of a place where the type of the delegate cannot be inferred.

 Dim o As Object ' Error: Delegate type cannot be inferred. o = AddressOf Class1.S2 

In this case, the exact type of the delegate that needs to be constructed is not clear, because the value is being assigned to Object . This would produce a compile-time error.

Delegates are multicast , which means that a single delegate can contain a reference to more than one method. The methods can be completely different, as long as they have exactly the same set of parameters and the same return type. A delegate can refer to more than one method when it is combined with another delegate using the System.Delegate.Combine method. For example:

 Class Button   Public Sub Move(ByVal x As Integer, ByVal y As Integer)     ...   End Sub End Class Class Form   Public Sub Move(ByVal x As Integer, ByVal y As Integer)     ...   End Sub End Class Module Test   Sub Main()     Dim s1, s2 As SubroutineDelegate     Dim b As Button = New Button()     Dim f As Form = New Form()     s1 = AddressOf b.Move     s2 = AddressOf f.Move     s1 = CType(System.Delegate.Combine(s1, s2), SubroutineDelegate)     s1(10,20)   End Sub End Module 

In this example, the delegate s1 ends up referring to both Button.Move and Form.Move . When the delegate is invoked, both methods are called, one after the other. The order in which the delegates are invoked is defined by the .NET Framework and cannot be relied upon.

Once delegates have been combined, the method System.Delegate.Remove can be used to break them apart. The following example only calls Button.Move because the reference to Form.Move has been removed from the delegate s1 .

 Module Test   Sub Main()     Dim s1, s2 As SubroutineDelegate     Dim b As Button = New Button()     Dim f As Form = New Form()     s1 = AddressOf b.Move     s2 = AddressOf f.Move     s1 = CType(System.Delegate.Combine(s1, s2), SubroutineDelegate)     s1 = CType(System.Delegate.Remove(s1, s2), SubroutineDelegate)     s1(10,20)   End Sub End Module 

Asynchronous Invocation

When a delegate is invoked normally, it is invoked synchronously . This means that while the delegate is being invoked, the method that invoked the delegate sits and waits for the delegate to finish. However, delegates can also be invoked asynchronously . That is, the delegate can be invoked in such a way that the method that invoked the delegate can continue executing while the delegate is being invoked. Asynchronous delegate invocation is accomplished through use of multiple threads of execution, a topic that is beyond the scope of this book. However, a quick discussion of how asynchronous delegate invocation works may be worthwhile.

Every delegate type contains two methods called BeginInvoke and EndInvoke that allow the delegate to be called asynchronously. The parameters to BeginInvoke are the parameters to the delegate itself, a callback delegate to invoke when the delegate is finished executing, and an argument that should be passed in to the callback delegate. BeginInvoke returns an instance of an object that implements System.IAsyncResult , which can be used to monitor the progress of the delegate call. Thus, a delegate declared as follows:

 Delegate Function CalculateDelegate(ByRef Iterations As Integer) _   As Integer 

would have a BeginInvoke call that looked like this.

 Function BeginInvoke(ByVal Iterations As Integer, _                      ByVal DelegateCallback As AsyncCallback, _                      ByVal DelegateAsyncState As Object) As                      IAsyncResult 

When you call BeginInvoke , the .NET Framework queues the delegate in the thread pool. When a thread in the thread pool becomes available, the delegate is executed on that thread and runs until the delegate returns. When the delegate returns, the result of the delegate, if any, is stored in the object returned from BeginInvoke .

When the delegate call has completed, or if the code invoking the delegate wishes to block until the delegate call completes, EndInvoke is called on the delegate. EndInvoke takes the IAsyncResult from the BeginInvoke call as an argument and returns the value from the delegate call, if any. It also takes parameters for all the ByRef parameters in the delegate call ”this allows the delegate the chance to pass back the reference parameters. Thus, the EndInvoke method for the CalculateDelegate example would look like this.

 Function EndInvoke(ByRef Iterations As Integer, _                    ByVal DelegateAsyncResult As IAsyncResult) As _                    Integer 

The following code shows an example of using BeginInvoke and EndInvoke to invoke a method asynchronously.

 Module Test   Function Calculate(ByRef Iterations As Integer) As Integer     Dim Result As Integer = 0     For Number As Integer = 1 To Iterations       Result += Number     Next Number     Return Result   End Function   Delegate Function CalculateDelegate(ByRef Iterations As Integer) _     As Integer   Sub Main()     Dim d As CalculateDelegate = AddressOf Calculate     Dim AsyncResult As IAsyncResult     Dim Result As Integer     Dim Iterations As Integer = 10000     AsyncResult = d.BeginInvoke(Iterations, Nothing, Nothing)     Console.WriteLine("Waiting for the result...")     Result = d.EndInvoke(Iterations, AsyncResult)     Console.WriteLine("The result is " & Result & ".")   End Sub End Module 
 <  Day Day Up  >  


The Visual Basic .NET Programming Language
The Visual Basic .NET Programming Language
ISBN: 0321169514
EAN: 2147483647
Year: 2004
Pages: 173
Authors: Paul Vick

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