You need a way of receiving
notification from an asynchronously invoked delegate that it has
finished. However, it must be more flexible than the notification
schemes in the previous two recipes (Recipe 15.4 and Recipe 15.5). This scheme must allow your code to continue
processing without having to constantly call
IsCompleted
in a loop or to rely on the
WaitOne
method. Since the asynchronous delegate
will return a value, you must be able to pass this return value back
to the invoking thread.
Use the
BeginInvoke
method to start the asynchronous
delegate, but use the first parameter to pass a
callback delegate to the
asynchronous delegate:
using System; using System.Threading; public class AsyncAction { public void CallbackAsyncDelegate( ) { AsyncCallback callBack = new AsyncCallback(DelegateCallback); AsyncInvoke method1 = new AsyncInvoke(TestAsyncInvoke.Method1); IAsyncResult asyncResult = method1.BeginInvoke(callBack, method1); // No need to poll or use the WaitOne method here, so return to the calling // method. return; } private static void DelegateCallback(IAsyncResult iresult) { AsyncResult asyncResult = (AsyncResult)iresult; AsyncInvoke method1 = (AsyncInvoke)asyncResult.AsyncDelegate; int retVal = method1.EndInvoke(asyncResult); Console.WriteLine("retVal (Callback): " + retVal); } }
This callback delegate will call the
DelegateCallback
method on the thread the method
was ultimately invoked on when the asynchronous delegate is finished
processing.
The following code defines the AsyncInvoke
delegate and the asynchronously invoked static method
TestAsyncInvoke.Method1
:
public delegate int AsyncInvoke( ); public class TestAsyncInvoke { public static int Method1( ) { Console.WriteLine("Invoked Method1"); return (1); } }
The asynchronous delegates in this recipe are created and invoked in
the same fashion as the asynchronous delegate in Recipe 15.4. Instead of using the
IsCompleted
property to determine when the
asynchronous delegate is finished processing (or the
WaitOne
method to block for a specified time while
the asynchronous delegate continues processing), this recipe uses a
callback to indicate to the calling thread that the asynchronous
delegate has finished processing and that its return value,
ref
parameter values, and out
parameter values are available.
Invoking a delegate in this manner is much more flexible and
efficient than simply polling the IsCompleted
property to determine when a delegate finishes processing. When
polling this property in a loop, the polling method cannot return and
allow the application to continue processing. A callback is also
better than using a WaitOne
method, since the
WaitOne
method will block the calling thread and
allow no processing to occur. You can break up the
WaitOne
method into a limited number of wait
cycles as in Recipe 15.5, but this is simply
a merging of the polling technique with the
WaitOne
operation.
The
CallbackAsyncDelegate
method in this recipe makes
use of the first parameter to the BeginInvoke
method of the asynchronous delegate to pass in another delegate that
contains a callback method to be called when the asynchronous
delegate finishes processing. After calling
BeginInvoke
, this method can now return and the
application can continue processing; it does not have to wait in a
polling loop or be blocked while the asynchronous delegate is
running.
The AsyncInvoke
delegate that is passed into the
first parameter of the BeginInvoke
method is
defined as follows:
public delegate void AsyncCallback(IAsyncResult ar
)
When this delegate is created, as shown here, the callback method
passed in, DelegateCallback
, will be called as
soon as the asynchronous delegate completes:
AsyncCallback callBack = new AsyncCallback(DelegateCallback);
DelegateCallback
will not run on the same thread
BeginInvoke
ran on. This callback method accepts a
parameter of type IAsyncResult
. You can cast this
parameter to an AsyncResult
object within the
delegate and use it to obtain information about the completed
asynchronous delegate, such as its return value, any
ref
parameter values, and any
out
parameter values. If the delegate instance
that was used to call BeginInvoke
is still in
scope, you can just pass the IAsyncResult
to the
EndInvoke
method. In addition, this object can
obtain any state information passed into the second parameter of the
BeginInvoke
method. This state information can be
any object type.
The DelegateCallback
method casts the
IAsyncResult
parameter to an
AsyncResult
object and obtains the asynchronous
delegate that was originally called. The EndInvoke
method of this asynchronous delegate is called to process any return
value, ref
parameters, or out
parameters. If any state object was passed in to the
BeginInvoke
method’s second
parameter, it can be obtained here through the following line of
code:
object state = asyncResult.AsyncState;