1.4 Delegate
1. Simple Delegates
A delegate in C# is a type that represents references to methods with a particular parameter list and return type. Delegates are used to pass methods as arguments to other methods. Here’s a simple example to illustrate how delegates work:
Example
using System;
namespace DelegateExample{ // Step 1: Define a delegate public delegate void SimpleDelegate(string message);
class Program { static void Main(string[] args) { // Step 2: Instantiate the delegate and assign a method to it SimpleDelegate del = new SimpleDelegate(PrintMessage);
// Step 3: Call the delegate del("Hello, this is a simple delegate example!");
// Alternatively, using method group conversion (no need to use 'new' keyword) SimpleDelegate del2 = PrintMessage; del2("This is another message using method group conversion.");
Console.ReadLine(); }
// A method that matches the delegate signature public static void PrintMessage(string message) { Console.WriteLine(message); } }}-
Delegate Definition (
SimpleDelegate):- The
SimpleDelegateis defined to match any method that takes a singlestringparameter and returnsvoid.
- The
-
Delegate Instantiation (
del):- A delegate instance
delis created and is assigned thePrintMessagemethod. This method matches the signature of the delegate.
- A delegate instance
-
Calling the Delegate (
del("Hello, this is a simple delegate example!")):- The delegate is called like a regular method. When the delegate is invoked, it calls the method assigned to it, passing the argument
"Hello, this is a simple delegate example!"to thePrintMessagemethod.
- The delegate is called like a regular method. When the delegate is invoked, it calls the method assigned to it, passing the argument
-
Method Group Conversion:
- Alternatively, you can assign a method to a delegate directly without using the
newkeyword, which is known as method group conversion.
- Alternatively, you can assign a method to a delegate directly without using the
Output
When you run this program, the output will be:
Hello, this is a simple delegate example!This is another message using method group conversion.2. Aysnc Delegate
The BeginInvoke and EndInvoke methods in .NET are used to asynchronously execute a delegate. Although the introduction of async and await in C# made these patterns less common, understanding them is useful for working with legacy code.
Here’s a simple example demonstrating how to use BeginInvoke and EndInvoke with a delegate:
Define a Delegate
First, define a delegate that represents a method signature.
using System;
namespace BeginInvokeExample{ // Define a delegate with the desired signature public delegate int AddDelegate(int a, int b);
class Program { static void Main(string[] args) { // Instantiate the delegate AddDelegate addDelegate = new AddDelegate(Add);
// BeginInvoke starts the asynchronous call IAsyncResult asyncResult = addDelegate.BeginInvoke(10, 20, AddCompleted, null);
// Do other work while the Add method runs asynchronously... Console.WriteLine("Doing other work...");
// Optionally, wait for the asynchronous call to complete int result = addDelegate.EndInvoke(asyncResult); Console.WriteLine($"Result: {result}");
Console.ReadLine(); }
// The method that matches the delegate signature static int Add(int a, int b) { // Simulate some work System.Threading.Thread.Sleep(2000); return a + b; }
// The callback method that is called when the async operation completes static void AddCompleted(IAsyncResult asyncResult) { Console.WriteLine("Addition operation completed."); } }}-
Delegate Definition (
AddDelegate): A delegateAddDelegateis defined to represent methods that take two integers and return an integer. -
Delegate Instantiation (
addDelegate): An instance of theAddDelegateis created and assigned theAddmethod, which matches the delegate signature. -
Asynchronous Call (
BeginInvoke):- The
BeginInvokemethod starts the asynchronous execution of theAddmethod. It takes the parameters required by theAddmethod, a callback method (AddCompleted), and an optional state object (passed asnullhere). BeginInvokeimmediately returns anIAsyncResult, allowing the main thread to continue executing other code while theAddmethod runs in the background.
- The
-
Callback (
AddCompleted):- The
AddCompletedmethod is a callback that runs when the asynchronous operation completes. Here, it simply prints a message indicating that the operation is done.
- The
-
EndInvoke:
- The
EndInvokemethod is used to retrieve the result of the asynchronous operation. It blocks until the operation is complete if it hasn’t finished yet.
- The
Output
When you run the program, the output will be:
Doing other work...Addition operation completed.Result: 303. AyncResult
The AsyncResult pattern was used in .NET for handling asynchronous operations before the introduction of async and await. It involves implementing the IAsyncResult interface and is often seen with the Begin/End method pair. Here’s a simple example that demonstrates how to use AsyncResult:
3.1 Example
Create a Simple Example Using the IAsyncResult Interface:
using System;using System.Threading;
namespace AsyncResultExample{ // A simple class to hold the state information. public class MyAsyncResult : IAsyncResult { private readonly ManualResetEvent _waitHandle; private readonly object _state;
public MyAsyncResult(object state) { _state = state; _waitHandle = new ManualResetEvent(false); }
// Properties required by IAsyncResult public object AsyncState => _state; public WaitHandle AsyncWaitHandle => _waitHandle; public bool CompletedSynchronously { get; private set; } public bool IsCompleted { get; private set; }
// Method to signal that the operation is complete public void Complete(bool completedSynchronously) { CompletedSynchronously = completedSynchronously; IsCompleted = true; _waitHandle.Set(); } }
public class MyAsyncClass { // Begin method that starts the asynchronous operation public IAsyncResult BeginOperation(string data, AsyncCallback callback, object state) { var asyncResult = new MyAsyncResult(state);
// Simulate an asynchronous operation using a thread ThreadPool.QueueUserWorkItem(_ => { // Simulate work Thread.Sleep(2000);
// Operation is complete asyncResult.Complete(false);
// Invoke the callback callback?.Invoke(asyncResult); });
return asyncResult; }
// End method that completes the asynchronous operation public string EndOperation(IAsyncResult result) { var myResult = (MyAsyncResult)result;
// Wait for the operation to complete if it hasn't already myResult.AsyncWaitHandle.WaitOne();
return $"Operation completed with state: {myResult.AsyncState}"; } }
class Program { static void Main() { var myAsyncClass = new MyAsyncClass();
// Start the asynchronous operation var result = myAsyncClass.BeginOperation("Hello, AsyncResult!", MyCallback, "User State");
// Do other work while the async operation is running...
// Wait for the operation to complete string operationResult = myAsyncClass.EndOperation(result); Console.WriteLine(operationResult);
Console.ReadLine(); }
// The callback method static void MyCallback(IAsyncResult result) { Console.WriteLine("Callback invoked."); } }}-
MyAsyncResult: This class implements theIAsyncResultinterface and holds the state information. It includes theAsyncState,AsyncWaitHandle,CompletedSynchronously, andIsCompletedproperties, which are required byIAsyncResult. -
BeginOperation: This method starts the asynchronous operation. It creates aMyAsyncResultinstance and simulates an operation usingThreadPool.QueueUserWorkItem. -
EndOperation: This method is called to retrieve the result of the asynchronous operation. It ensures that the operation is complete before returning the result. -
Callback Method: The callback method (
MyCallback) is invoked once the asynchronous operation is complete. It receives theIAsyncResultinstance as a parameter.
Output
When you run the program, it will:
- Start the asynchronous operation.
- Invoke the callback method when the operation is complete.
- Retrieve and print the result using the
EndOperationmethod.
In the example above where asyncResult.Complete(false); was used, this line of code serves to mark the asynchronous operation as complete and to indicate whether the operation was completed synchronously or asynchronously.
3.1.1 Explanation of Complete(false)
The Complete(bool completedSynchronously) method in the MyAsyncResult class is designed to:
-
Mark the Operation as Complete: By setting
IsCompletedtotrueand signaling theManualResetEvent(using_waitHandle.Set()), this method informs any waiting threads or callback methods that the asynchronous operation has finished. -
Indicate Whether the Operation Was Completed Synchronously: The
completedSynchronouslyparameter is aboolthat specifies whether the operation was completed synchronously (true) or asynchronously (false).- If
completedSynchronouslyistrue, it means that the operation was completed on the same thread that initiated the operation, without any delay. - If
completedSynchronouslyisfalse, it means that the operation was completed asynchronously, possibly on a different thread.
- If
3.1.2 Why Complete(false)?
In the provided example:
- The asynchronous operation is simulated using
ThreadPool.QueueUserWorkItem, which runs the operation on a different thread. - Therefore, the operation is completed asynchronously, so
completedSynchronouslyis set tofalseinasyncResult.Complete(false);.
Key Points
-
Synchronous vs. Asynchronous Completion: The distinction between synchronous and asynchronous completion is important for consumers of the
IAsyncResultinterface. Some consumers might handle synchronous completions differently, such as skipping the need to callEndInvokeimmediately because the result is already available. -
Callback Handling: When
Complete(false)is called, it ensures that the callback provided in theBeginOperationmethod is invoked after the asynchronous operation is complete, allowing the main thread or other threads to handle the result as needed.