Delegates and Events—Callback Methods

Jump to: navigation, search
Visual C# Tutorials

Delegates and Events

© 2005 O'Reilly Media, Inc.

Callback Methods

You accomplish this goal of delegating work and being called back when it is done with a callback, which you implement with (surprise!) a delegate. The .NET Framework provides a callback mechanism by defining the ASyncCallBack delegate:

[Serializable]
public delegate void AsyncCallback(
   IAsyncResult ar
);

The attribute (Serializable) is covered in Chapter 18. You can see here, however, that AsyncCallBack is a delegate for a method that returns void and takes a single argument, an object of type IAsyncResult. This interface is defined by the Framework, and the CLR will be calling your method with an object that implements the interface, so you don’t need to know the details of the interface; you can just use the object provided to you.

Here’s how it works. You will ask the delegate for its invocation list, and you will call BeginInvoke on each delegate in that list. BeginInvoke will take two parameters. The first will be a delegate of type AsyncCallBack, and the second will be your own delegate that invokes the method you want to call:

del.BeginInvoke(new AsyncCallback(ResultsReturned),del);

In the line of code shown here, you are calling the method encapsulated by del (e.g., DisplayCounter) and when that method completes, you want to be notified via your method ResultsReturned.

The method to be called back (ResultsReturned) must match the return type and signature of the AsyncCallback delegate: it must return void and must take an object of type IAsyncResult:

private void ResultsReturned(IAsyncResult iar)
{

When that method is called back, the IAsyncResult object is passed in by the .NET Framework. The second parameter to BeginInvoke is your delegate, and that delegate is stashed away for you in the AsyncState property of the IAsyncResult as an Object. Inside the ResultsReturned callback method, you can extract that Object and cast it to its original type:

DelegateThatReturnsInt del = (DelegateThatReturnsInt)iar.AsyncState;

You can now use that delegate to call the EndInvoke() method, passing in the IAsyncResult object you received as a parameter:

int result = del.EndInvoke(iar);

EndInvoke() returns the value of the called (and now completed) method, which you assign to a local variable named result, and which you are now free to display to the user.

The net effect is that in Run(), you get each registered method in turn (first FirstSubscriber.DisplayCounter and then SecondSubscriber.Doubler), and you invoke each asynchronously. There is no delay between the call to the first and the call to the second, as you aren’t waiting for DisplayCounter to return.

When DisplayCounter (or Doubler) has results, your callback method (ResultsReturned) is invoked, and you use the IAsyncResult object provided as a parameter to get the actual results back from these methods. The complete implementation is shown in Example 12-6.

Example 12-6. Asynchronous invocation of delegates

#region Using directives
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
 
#endregion
 
namespace AsynchDelegates
{
   public class ClassWithDelegate
   {
      // a multicast delegate that encapsulates a method
      // that returns an int
      public delegate int DelegateThatReturnsInt();
      public event DelegateThatReturnsInt theDelegate;
      public void Run()
      {
         for ( ; ; )
         {
            // sleep for a half second
            Thread.Sleep( 500 );
            if ( theDelegate != null )
            {
               // explicitly invoke each delegated method
               foreach (
                  DelegateThatReturnsInt del in
                  theDelegate.GetInvocationList() )
               {
                  // invoke asynchronously
                  // pass the delegate in as a state object
                  del.BeginInvoke( new AsyncCallback( ResultsReturned ),
                     del );
               } // end foreach
            } // end if
         } // end for ;;
      } // end run
 
      // call back method to capture results
      private void ResultsReturned( IAsyncResult iar )
      {
         // cast the state object back to the delegate type
         DelegateThatReturnsInt del =
            ( DelegateThatReturnsInt ) iar.AsyncState;
 
         // call EndInvoke on the delegate to get the results
         int result = del.EndInvoke( iar );
 
         // display the results
         Console.WriteLine( "Delegate returned result: {0}", result );
      }
   } // end class
 
   public class FirstSubscriber
   {
      private int myCounter = 0;
      public void Subscribe( ClassWithDelegate theClassWithDelegate )
      {
         theClassWithDelegate.theDelegate +=
         new ClassWithDelegate.DelegateThatReturnsInt( DisplayCounter );
      }
   
      public int DisplayCounter()
      {
         Console.WriteLine( "Busy in DisplayCounter..." );
         Thread.Sleep( 10000 );
         Console.WriteLine( "Done with work in DisplayCounter..." );
         return ++myCounter;
      }
   }
 
   public class SecondSubscriber
   {
      private int myCounter = 0;
      public void Subscribe( ClassWithDelegate theClassWithDelegate )
      {
         theClassWithDelegate.theDelegate +=
            new ClassWithDelegate.DelegateThatReturnsInt( Doubler );
      }
   
      public int Doubler()
      {
         return myCounter += 2;
      }
   }
 
   public class Test
   {
      public static void Main()
      {
         ClassWithDelegate theClassWithDelegate =
            new ClassWithDelegate();
         FirstSubscriber fs = new FirstSubscriber();
         fs.Subscribe( theClassWithDelegate );
         SecondSubscriber ss = new SecondSubscriber();
         ss.Subscribe( theClassWithDelegate );
         theClassWithDelegate.Run();
      }
   }
}


Read the book review!


prevpp.png  nextpp.png
C# Online.NET