Events


Unfortunately, the universe, being very busy and unaccustomed to paying attention to individuals, had managed to replace Peter's boss's delegate with its own. This was an unintended side effect of making the delegate fields public in Peter's Worker class. Likewise, if Peter's boss got impatient, he could decide to fire Peter's delegates himself (which is just the kind of rude thing that Peter's boss was apt to do):

 
 ' Peter's boss taking matters into his own hands If Not (peter.completed Is Nothing) Then peter.completed() 

Peter wanted to make sure that neither of these could happen. He realized that he needed to add registration and unregistration functions for each delegate so that listeners could add or remove themselves but couldn't clear the entire list or fire Peter's events. Instead of implementing these functions himself, Peter used the event keyword to make the Visual Basic .NET compiler build these methods for him:

 
 Class Worker   ...   Public Event started As WorkStarted   Public Event progressing As WorkProgressing   Public Event completed As WorkCompleted End Class 

Peter knew that the event keyword erected a property around a delegate, allowing only clients to add or remove themselves (using the AddHandler and RemoveHandler methods, if the client was written in Visual Basic .NET), forcing his boss and the universe to play nicely , and he would be thrilled if he could simply use the following code:

 
 Shared Sub Main()   Dim peter As Worker = New Worker()   Dim theboss As Boss = New Boss()   AddHandler peter.completed, AddressOf boss.WorkCompleted   AddHandler peter.started, AddressOf Universe.WorkerStartedWork   AddHandler peter.completed, AddressOf Universe.WorkerCompletedWork   peter.DoWork()   Console.WriteLine("Main: worker completed work")   Console.ReadLine End Sub 

Unfortunately for Peter, he had just run up against a limitation in Visual Basic .NET: Events in Visual Basic .NET could not expose a return type; they had to be Subs, and never Functions. The preceding code worked beautifully for the started and progressing events, but threw a compilation error on completed. Peter had two choices: to eliminate the return value from completed, or control registrations to the delegate form of completed himself. Peter chose to use the multicast delegate form to register multiple listeners to a single event:

 
 Shared Sub Main()   Dim peter As Worker = New Worker()   Dim theboss As Boss = New Boss()   ' Get handlers to listeners for completed event   Dim d1, d2 As WorkCompleted   d1 = AddressOf theboss.WorkCompleted   d2 = AddressOf Universe.WorkerCompletedWork   ' Use Shared Combine method of Delegate ' class to assign both to delegate   peter.completed = _ CType(System.Delegate.Combine(d1, d2), WorkCompleted)   ' Use AddHandler method for started Event   AddHandler peter.started, AddressOf Universe.WorkerStartedWork   ' Do work   peter.DoWork()   Console.WriteLine("Main: worker completed work")   Console.ReadLine End Sub 

Harvesting All Results

At this point, Peter breathed a sigh of relief. He had managed to satisfy the requirements of all his subscribers without having to be closely coupled with the specific implementations . However, he noticed that although both his boss and the universe provided grades of his work, Peter was receiving only one of the grades. In the face of multiple subscribers, he really wanted to harvest all of their results. So, he reached into his delegate and pulled out the list of subscribers so that he could call each of them manually:

 
 Public Sub DoWork()   ...   Console.WriteLine("Worker: work completed")   If Not (completed Is Nothing) Then       Dim wc As WorkCompleted       For Each wc In completed.GetInvocationList()           Dim grade As Integer = wc()           Console.WriteLine("Worker grade=" & grade.ToString())       Next   End If End Sub 

Asynchronous Notification: Fire and Forget

In the meantime, his boss and the universe had been distracted with other things, and this meant that the time it took them to grade Peter's work had been greatly expanded:

 
 Class Boss   Public Function WorkCompleted() As Integer       System.Threading.Thread.Sleep(3000)       Console.WriteLine("Better...")       Return 6 ' out of 10   End Function End Class Class Universe   Shared Function WorkerCompletedWork() As Integer       System.Threading.Thread.Sleep(4000)       Console.WriteLine("Universe is pleased with worker's work")       Return 7 ' out of 10   End Function End Class 

Unfortunately, because Peter was notifying each subscriber one at a time, waiting for each to grade him, these notifications now took up quite a bit of his time when he should have been working. So, he decided to forget the grade and just fire the event asynchronously:

 
 Public Sub DoWork()   ...   Console.WriteLine("Worker: work completed")   If Not(completed Is Nothing) Then       Dim wc As WorkCompleted       For Each wc In completed.GetInvocationList()           wc.BeginInvoke(Nothing, Nothing)       Next   End If End Sub 

Asynchronous Notification: Polling

This clever trick allowed Peter to notify the subscribers while letting him get back to work immediately, letting the process thread pool invoke the delegate. Over time, however, Peter found that he missed the feedback on his work. He knew that he did a good job and appreciated the praise of the universe as a whole (if not his boss specifically ). So, he fired the event asynchronously but polled periodically, looking for the grade to be available:

 
 Public Sub DoWork()   ...   Console.WriteLine("Worker: work completed")   If Not (completed Is Nothing) Then       Dim wc As WorkCompleted     For Each wc In completed.GetInvocationList()       Dim res As IAsyncResult = wc.BeginInvoke(Nothing, Nothing)        While Not res.IsCompleted          System.Threading.Thread.Sleep(1)        End While        Dim grade As Integer = wc.EndInvoke(res)        Console.WriteLine("Worker grade= " & grade.ToString())     Next   End If End Sub 

Asynchronous Notification: Delegates

Unfortunately, Peter was back to the very thing he wanted his boss to avoid with him in the beginning: looking over the shoulder of the entity doing the work. So, he decided to employ another delegate as a means of notification when the asynchronous work was completed, allowing him to get back to work immediately but still be notified when his work had been graded:

 
 Public Sub DoWork()   ...   Console.WriteLine("Worker: work completed")   If Not (completed Is Nothing) Then       Dim wc As WorkCompleted     For Each wc In completed.GetInvocationList()       Dim res As IAsyncResult = wc.BeginInvoke(New AsyncCallback( _               AddressOf WorkGraded), wc)      Next   End If End Sub Public Sub WorkGraded(ByVal res As IAsyncResult)   Dim wc As WorkCompleted = CType(res.AsyncState, WorkCompleted)   Dim grade As Integer = wc.EndInvoke(res)   Console.WriteLine("Worker grade= " & grade.ToString()) End Sub 


Windows Forms Programming in Visual Basic .NET
Windows Forms Programming in Visual Basic .NET
ISBN: 0321125193
EAN: 2147483647
Year: 2003
Pages: 139

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