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 was just the kind of rude thing that Peter's boss was apt to do):

// Peter's boss taking matters into his own hands if( peter.Completed != null ) peter.Completed();


Peter wanted to make sure that neither of these things 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 C# compiler build these methods for him:

class Worker {   public event WorkStarted Started;   public event WorkProgressing Progressing;   public event WorkCompleted Completed;   ... }


Peter knew that the event keyword erected a property around a delegate, allowing only clients to add or remove themselves (using the += and = operators in C#), forcing his boss and the universe to play nicely:

class Universe {   ...   static void Main() {     Worker peter = new Worker();     Boss boss = new Boss();        peter.Completed = boss.WorkCompleted; // ERR!     peter.Completed += boss.WorkCompleted; // OK     peter.Started += Universe.WorkerStartedWork; // OK     peter.Completed += Universe.WorkerCompletedWork; // OK     peter.DoWork();     Console.WriteLine("Main: worker completed work");     Console.ReadLine();   } }


Harvesting All Results

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

class Worker {   ...   public void DoWork() {     ...     Console.WriteLine("Worker: work completed");     if( this.Completed != null ) {       foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {         int grade = wc();         Console.WriteLine("Worker grade= {0}", grade);       }     }   } }


Asynchronous Notification: Fire and Forget

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

class Boss {   public int WorkCompleted() {     System.Threading.Thread.Sleep(5000);     Console.WriteLine("Better...");     return 4; // out of 10   } } class Universe {   ...   static int WorkerCompletedWork() {     System.Threading.Thread.Sleep(1000000);     Console.WriteLine("Universe pleased with worker's work");     return 7;   }   ... }


Unfortunately, since Peter was notifying each listener 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:

class Worker {   ...   public void DoWork() {     ...     Console.WriteLine("Worker: work completed");     if( this.Completed != null ) {       foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {         wc.BeginInvoke(null, null); // EndInvoke call required by .NET       }     }   } }


Asynchronous Notification: Polling

The call to BeginInvoke allowed Peter to notify the listeners while letting Peter 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). Plus, he was afraid that he was leaking .NET resources acquired by calling BeginInvoke without calling the corresponding EndInvoke method. So, he fired the event asynchronously but polled periodically, looking for the grade to be available:

class Worker {   ...   public void DoWork() {     ...     Console.WriteLine("Worker: work completed");     if( this.Completed != null ) {       foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {         IAsyncResult result = wc.BeginInvoke(null, null);         while( !result.IsCompleted ) System.Threading.Thread.Sleep(1);         int grade = wc.EndInvoke(result);         Console.WriteLine("Worker grade= {0}", grade);       }     }   } }


Asynchronous Notification: Delegates

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

class Worker {   ...   public void DoWork() {     ...     Console.WriteLine("Worker: work completed");     if( this.Completed != null ) {       foreach( WorkCompleted wc in this.Completed.GetInvocationList() ) {         wc.BeginInvoke(this.WorkGraded, wc);       }     }   }   void WorkGraded(IAsyncResult result) {     WorkCompleted wc = (WorkCompleted)result.AsyncState;     int grade = wc.EndInvoke(result);     Console.WriteLine("Worker grade= {0}" + grade);   } }





Windows Forms 2.0 Programming
Windows Forms 2.0 Programming (Microsoft .NET Development Series)
ISBN: 0321267966
EAN: 2147483647
Year: 2006
Pages: 216

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