Audience
This article is intended for people who have basic knowledge about delegates in C#.
Introduction
We all know about the word delegate in the .NET world. But what will the compiler do when seeing the word delegate in your program. The compiler and the CLR do a lot of behind the scenes processing to hide the complexity. I'll focus on how the compiler and the CLR work together to implement delegates. This article will explain to you about the complete class of delegate and the asynchronous processing performed by the delegate.
Using the Code
public delegate void MyDelegate(int intValue);
The compiler actually defines a complete class that looks something like this:
class MyDelegate : System.MulticastDelegate
{
public MyDelegate(Object object, Inptr method);
public virtual void Invoke(Int32 intValue);
public virtual IAsyncResult BeginInvoke(Int32 intValue,
AsyncCallback callback, object object);
public virtual void EndInvoke(IAsyncResult result);
}
ILdasm.exe displaying the metadata produced by the compiler for the delegate.
The class defined by the compiler has four methods:
- constructor
Invoke
method
BeginInvoke
method
EndInvoke
method
Invoke
method is used to call the target method in the same thread. It is a synchronous process. I will explain to you about the BeginInvoke
and EndInvoke
methods later in this article. Before that, we want to know about how the methods (static and instance) are pointed internally in delegate, probably this is the most significant feature in delegates. There are three non public fields:
_target
When a delegate object wraps a static
method, this field is null
. When the delegate objects wraps the instance method, this field refers to the object.
_methodptr
It is used to identify the method that is to be called back.
_invocationlist
This field is usually null
, it can refer to an array of delegates when building a delegate chain.
MyDelegate objStatic = new MyDelegate(Class.StaticMethod);
MyDelegate objInstance = new MyDelegate(new Class().InstanceMethod);
The _target
field holds the System.Object
type. A reference to the object is passed for the constructor's object parameter, and a special intptr
value that identifies the method is passed for the method parameter. For static
methods, null
is passed for the object parameter. Inside the constructor, these two arguments are saved in the _target
and _methodptr private
fields, respectively.
Internally, the combine
method sees that objChain
already refers to a delegate object, so combine
will construct a new delegate object. This new delegate object initializes its private _target
and _methodptr
fields to values that are out of scope here. What is important is that the _invocationlist
field is initialized to refer to an array of delegate objects. The first element of the array (index 0) will be initialized to refer to the delegate that wraps the static
method. The second element of the array (index 1) will be initialized to refer to the delegate that wraps the Instance method.
MyDelegate objStatic1 = new MyDelegate(Class.StaticMethod1);
MyDelegate objInstance2 = new MyDelegate(new Class().InstanceMethod2);
MyDelegate objChain = null;
objChain = (MyDelegate) Delegate.Combine( objChain, objStatic1);
objChain = (MyDelegate) Delegate.Combine( objChain, objInstance2);
Asynchronous Delegates
Delegates can call the methods in an asynchronous manner. If the BeginInvoke
method is called, then the CLR will queue the request and return immediately to the main thread. The target method will be called on a thread from the thread pool. The main thread can perform parallel execution to the target method. BeginInvoke
method has three parameters:
- Input parameter
AsyncCallBack
delegate instance
- Async State information
objDel.BeginInvoke(5,new AsyncCallback(MyCallback),"Result From Main Program");
The last two parameters are used to provide a callback mechanism that will be invoked when the target method completes. This is called AsyncCallBack
delegate.
delegate void AsyncCallback( IAsyncResult ar );
If the callback was not specified in the BeginInvoke
then the EndInvoke
is used in the original thread. It is used to obtain the return value for the asynchronous processing. If the callback was present, then the EndInvoke
is placed in the callback method.
Below is the sample program for Asynchronous delegate:
public delegate int MyDelegate(int intX);
class Program
{
static void Main(string[] args)
{
MyClass objClass = new MyClass();
MyDelegate objDel = new MyDelegate(objClass.MyMethod);
IAsyncResult AsyncRes = objDel.BeginInvoke
(5, new AsyncCallback(MyCallBack), "State info from main thread");
Console.WriteLine("From main thread ");
Console.ReadLine();
}
public static void MyCallBack(IAsyncResult ar)
{
AsyncResult Result = (AsyncResult)ar;
MyDelegate objDel = (MyDelegate)Result.AsyncDelegate;
int intResult = objDel.EndInvoke(ar);
Console.WriteLine("Output from callback: " + intResult);
string strMessage = (string)ar.AsyncState;
Console.WriteLine(strMessage);
}
}
class MyClass
{
public int MyMethod(int intX)
{
Thread.Sleep(10000);
return (intX * intX);
}
}
Output
Conclusion
Delegate is an object which refers to methods. It can be used in asynchronous or synchronous processes by running the methods in another threads. The asynchronous process is mainly useful for background processing like mailing, database transaction, etc. so the user may not wait until the operation completes.
History
- 23rd March, 2009: Initial post