Delegate: C# (Digging, Explain, Use it)






4.70/5 (17 votes)
Delegate in C# (Digging and explain the internals of Delegate)
Overview
.NET provides one nice characteristic, that is delegate
. Delegate
is an object or instance which can hold reference of any function OR which can bind function. Referenced function may be static
or any class object’s function. This can hold more than one function references. This also provides callback method. From a developer’s perspective, Delegate
is two type single cast and multicast.
Every delegate base class is System.MulticastDelegate. System.MulticastDelegate class is inherited from delegate class. Both classes are abstract classes.
.NET provides a standard constructor as follows:
Constructor for MultiCastDelegate
:
protected MulticastDelegate(object target, string method);
Constructor for Delegate
:
protected Delegate(object target, string method);
Declare Delegate
Declaring of the delegation is quite tricky, because when we declare delegation. We inform the compiler which kind of function signature’s reference could be held by this delegate. Let’s explain by the following: Fig-1.0
Look at FIG-1.0:
public
is Access modifierdelegate
is keyword which indicates this is delegate type- Return type of function signature
- Delegate class name (Explain below)
- Parameter of function signature
- Parameter of function signature
3, 5, 6 are optional. Because these are depending on function signature, which function type we want bind or wrap with delegate.
Let's look at one more example:
protected delegate string DelegateReverse (string StrText) ;
It means this delegate
class object can bind a function which function return type should be string
with one string parameter. DelegateReverse
is delegate
class name.
Question: Look at Fig-1 DelegateFunction
what is this? Is this is an object or name of the delegate
.
Answer: This is sealed
class. Look at the following example, then we describe:
//Code Sample - 1.0
using System;
using System.Collections.Generic;
using System.Text;
namespace DelegateCode
{
class DelExample
{
// Delegate declaration.
public delegate string StringOperation(string myString);
static void Main(string[] args)
{
// Create instance of class for accessing its function
DelExample objEx = new DelExample();
// create instance of Delegate Class
StringOperation Objdelegate =
new StringOperation(objEx.MyOperationReverseString);
// Call delegate object and store result in appropriate variable
string result = Objdelegate("ABC DEF GHI");
// Get Type
Type Tp = Objdelegate.GetType();
// Output
Console.WriteLine("Is this Class : " + Tp.IsClass.ToString()+ "\n");
Console.WriteLine("Inherit from : " + Tp.BaseType.ToString() + "\n");
Console.WriteLine("Is Abstract : " + Tp.IsAbstract.ToString() + "\n");
Console.WriteLine("Is ByRef : " + Tp.IsByRef.ToString() + "\n");
Console.WriteLine("Is Sealed : " + Tp.IsSealed.ToString() + "\n");
Console.WriteLine("Output of Delegate : " + result+"\n");
Console.WriteLine("Output by Invoke : " + Objdelegate.Invoke("ABC DEF GHI"));
Console.Read();
}
// definition of the function MyOperationReverseString
public string MyOperationReverseString(string test)
{
char[] Spl = test.ToCharArray();
test = string.Empty;
for (int icnt = 0; icnt < Spl.Length; icnt++)
{
test = Spl[icnt] + test;
}
return test;
}
}
}
Consider Fig – 2.0 output. In the above code, the following line...
public delegate string StringOperation(string myString);
... StringOperation is sealed
class. This is inherited by System.MultiCastDelegate
and is not abstract
.
Look at the last two lines of output. We can call delegate
by invoke
method or by passing parameter without invoke
method.
How C# Compiler Understands
Whenever compiler encountered delegate declaration like:
public delegate string StringOperation(string myString);
Then compiler generates the following code:
public sealed class StringOperation: System.MulticastDelegate
{
public StringOperation (object target, int method);
public virtual void Invoke(string myString);
public virtual IAsyncResult BeginInvoke(string myString,
AsyncCallback callback, object obj);
public virtual void EndInvoke(IAsyncResult result);
}
You can see .ctor: void (Object ,native int)
(This is a similar constructor as System.MulticastDelege
constructor.)
Invoke
method should have the same signature of the referenced function.
History Delegate Cannot Declare and Use as Field
//Code Sample – 2.0:
namespace DelegateExample
{
public delegate string MyDelegate(string myString);
interface MyCallable
{
int GetInfo();
delegate int CalcArea(int x, int y); // Error : interfaces cannot declare types
MyDelegate ObjDelegae; // Error : Interfaces cannot contain fields
}
}
//Code Sample – 3.0:
public void DispachCall(int tStr)
{
public delegate string MyDelegate(string myString); // Error
}
History Difference b/w Single and Multicast Delegate
From the developer’s perspective, Delegate
is two type single cast and multicast, although ".NET" has provided two classes for the delegate.
Singlecast delegate vs Multicast delegate do not have any declaration and implementation differences. This is only perspective.
Even I read more articles related to delegate
s, they said old Multicast delegate cannot wrap / bind a function which returns value. But I have never seen like this.
Delegate
class (Abstract
class)MulticastDelegate
(Abstract
class inherited byDelegate
class)
Every delegate in the .NET is a Multicast delegate. Then…
Question: So what is Singlecast Delegate
?
Answer: If delegate
object has only one function’s reference, then it calls single cast delegate
. If delegate
object references more than one function, this is called MulticastDelegate
.
When delegate object refers or binds multiple function references, then all functions reference store as linked list. All functions will call in sequence as they are assigned.
Example: Let’s we have delegate
object ObjDel
.
ObjDel
binds or wraps three functions in sequence respectively Add
, Mul
, Divide
. All three functions return integer type.
Let’s calling delegate
:
Int result = ObjDel.invoke(15,5);
It will return 3
.
If function wraps in sequence Add
, Divide
, Mul
.
Result = ObjDel.invoke(15,5).
It will return 75
.
It always returns the value of last function. So what about the other function return values ?
Keypoint
If any delegate object binds or wraps 5 return
type functions. Then only 5th functions will return value but rest of the four functions could not return value. So always end of calling function returns value. Rest four functions return value will ignored.
Delegate Asynchronously
Note: Simple overview for delegate as asynchronous call. More will be explained in the next article
Recall Fig-3.0, there is IL-DASM view. StringOperation
delegate has Invoke
and BeginInvoke
functions. These two functions are created by compiler at run time.
In C#, we can achieve asynchronous function call using delegate
.
By default, every function in C# is synchronous. But we can call synchronous function as asynchronous.
The Invoke
method calls the target method on the current thread. But BeginInvoke
method is called with a different thread, the common language runtime (CLR) queues the request and returns immediately to the caller.
BeginInvoke
can be called by only single cast delegate otherwise it gets run time exception (ArgumentException
) which is “The delegate must have only one target”.
About BeginInvoke
.NET implements BeginInvoke
function for each Delegate
class. So whenever compiler encountered delegate
declaration, it implements three methods for each delegate
class.
Invoke
, BeginInvoke
and EndInvoke
. Each BeginInvoke
has a corresponding EndInvoke
.
Key Point
Always call EndInvoke
to complete your asynchronous call. The technique of the calling does not matter.
BeginInvoke
have three parameters. BeginInvoke
returns IAsyncresult
.
S.No | Description | |
1. | Parameters of the function which calling | |
2. | AsynchCallback |
. delegate void AsyncCallback( IAsyncResult ar ); |
3. |
System.Object |
AsyncCallback (IAsyncResult Ar)
Key Point
IAsyncresult
is used for monitoring state or progress of the asynchronous function call. This is returned by BeginInvoke
and it is also the parameter for AsyncCallback
function. Its declaration is.
public interface IAsyncResult
{
object AsyncState{ get; }
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted { get; }
}
Key Point
Call delegate using Invoke
method. Then it’s run in the same thread.
Look at the following simple code. I called the same delegate using Invoke
and BeginInvoke
method. And write thread ID for both calls.
Code Sample – 4.0
using System;
using System.Threading;
namespace TestMultipuleDelegate
{
class MultiCastTest : TestMultipuleDelegate.IMultiCastTest
{
delegate int Calc(int x, int y,out int id);
static void Main(string[] args)
{
int thid = 0;
MultiCastTest ClObj = new MultiCastTest();
Calc objCalc = new Calc(ClObj.Add);
// Get current thread ID
thid = AppDomain.GetCurrentThreadId();
Console.WriteLine(" ");
Console.WriteLine(" Main Application Thread ID : " +
thid.ToString()); Console.WriteLine();
// Call delegate by Invoke method (synchronous call
int Result = objCalc.Invoke(8, 8, out thid);
Console.WriteLine("Invoke method completed "); Console.WriteLine();
// Call delegate by BeginInvoke : Asynchronous call
IAsyncResult rs = objCalc.BeginInvoke(234, 6, out thid, null,null);
Console.WriteLine("Begin Invoke has been start."); Console.WriteLine();
// Calling continue Mul funtion along Main thread.
Console.WriteLine( "Resule Mul " + ClObj.Mul(10, 8).ToString());
// Waiting till completed the Asynchronous call completed.
while (rs.IsCompleted == false)
{
Console.WriteLine("On processing..."); Console.WriteLine();
Thread.Sleep(1000);
}
//// Get the result of Asynchronous ,calling functions
Result = objCalc.EndInvoke(out thid,rs);
Console.WriteLine("Result : " + Result.ToString()); Console.WriteLine();
Console.WriteLine("Process completed"); Console.WriteLine();
Console.ReadKey();
}
public int Add(int x, int y,out int thid)
{
Console.WriteLine("In side Calling function Add : "); Console.WriteLine();
// Get calling function thread ID
thid = AppDomain.GetCurrentThreadId();
Console.WriteLine(" Calling function Thread ID : " + thid.ToString());
Console.WriteLine();
Thread.Sleep(1000);
return x + y;
}
public int Mul(int a, int b)
{
Console.WriteLine("Inside Multipule function thred ID. "+
AppDomain.GetCurrentThreadId().ToString()); Console.WriteLine();
return a*b;
}
}
}
// Another .CS file
using System;
namespace TestMultipuleDelegate
{
interface IMultiCastTest
{
int Add(int x, int y, out int thid);
int Mul(int a, int b);
}
}
We can see at Fig-4.0: (Code Sample – 4.0 outputs).
Main thread id = Invoke
method function calls thread id = simple
function calls immediately after BeginInvoke
function call. But BeginInvoke
function call thread id is 2992 different.
Some Facts about the Delegate
Delegate
is an object and by defaultprivate
type access modifier.Delegate
class is generated by C# compiler according its declaration.Delegate
class object can hold reference ofstatic
or any class object’s function.Delegate
class is asealed
class which is generated by the C# compiler during run time.Delegate
is a keyword, so any function cannot returndelegate
type.Delegate
cannot use as field of the class.Delegate
provides asynchronous and synchronous calls.
History
- 20th July, 2011: Initial post