Monday, May 26, 2008

Delegates in C#

Pointer is a variable, which stores address of some memory location. The memory location can be the storage for some value or beginning of function. When it points to function, it is function pointer. Function pointer has address of the function.

Function pointers are not safe. In thousand lines of code, if we have to invoke the function using function pointer, we cannot assure that the pointer points to function without going through the code.
We cannot ensure the number of parameters to the function or the order of parameters. Order of parameters refer to data types. What will happen if we invoked a function with function pointer & pointer is not pointing to function? What will happen if we invoked a function with function pointer & number of arguments are more or less passed? What will happen if we invoked a function with function pointer & order of parameters is wrong? (e.g. Function accepts int first & we pass string first.)

In all cases, our application will crash. In essence, Function pointers are not type-safe.

.NET has got a concept of delegates which are type-safe function pointers with ability to point to multiple functions. Delegates are declared in C# using keyword delegate.
public delegate void DisplayDelegate(string msg);

Declaration of delegate is just like function declaration. That is, the delegate must be declared with return data type & parameters. Parameter names are not relevant.

Internally, a class "DisplayDelegate" is generated which derives from System.MulticastDelegate. System.MulticastDelegate derives from System.Delegate. System.Delegate is the base class. One can create the object of "DisplayDelegate" class. The constructor accepts the function which has same signature as that of delegate declaration.
using System;

namespace DelegateDemo
{
public delegate void DisplayDelegate(string msg);
class Program
{
static void Main(string[] args)
{
DisplayDelegate del = new DisplayDelegate(Show);
del("Hello, delegate!!!");
//Or del.Invoke("Hello, delegate!!!");
}
static void Show(string str)
{
Console.WriteLine(str);
}
}
}
To invoke function using delegate, use Invoke function on delegate object with parameter values for the function. The function wrapped in delegate can be static or instance function.

Let's see, how delegates are type-safe? When one creates a delegate object, the delegate is aware of the parameters required. Wherever you call the function using delegate, compiler will not compile the program if,

  • function specified in constructor is not with appropriate function signature.
  • the number of parameters are less or more while invoking the function.
  • the order of parameters is not appropriate.
In essence, delegates are type-safe. One can catch the errors at compile-time only using delegates

A single delegate can have multiple functions. All functions must have same signature like delegate. Delegate invokes functions in FIFO(First-In,First-Out) order. If the function has return value, return value of last function invoked will be returned by delegate. Return values of all other functions are lost.

System.Delegate class has two static methods Combine and Remove. Use the methods to provide the delegate with multiple functions.
using System;
using System.Windows.Forms;

namespace DelegateDemo
{
public delegate void DisplayDelegate(string msg);
class Program
{
static void Main(string[] args)
{
DisplayDelegate del = new DisplayDelegate(Show);
DisplayDelegate del1 = new DisplayDelegate(Print);
del = (DisplayDelegate)Delegate.Combine(del, del1);
del("Hello,World!!!");
del = (DisplayDelegate)Delegate.Remove(del, del1);
del("Hi!!!");
}
static void Show(string str)
{
MessageBox.Show(str);
}
static void Print(string msg)
{
Console.WriteLine(msg);
}
}
}

System.Delegate has two properties Method and Target. The derived class have these properties inherited. "Method" property gets the method represented by delegate. "Target" gets the instance/object on which the delegate operates. "Target" returns null for static methods.
static void Main(string[] args)
{
DisplayDelegate del = new DisplayDelegate(Show);
Console.WriteLine(del.Method.Name);
if(del.Target!=null)
Console.WriteLine(del.Target.ToString());
}

If delegate has multiple functions, one can use GetInvocationList method of System.Delegate class to retrieve System.Delegate[] array.

static void Main(string[] args)
{
DisplayDelegate del = new DisplayDelegate(Show);
DisplayDelegate del1 = new DisplayDelegate(Print);
del = (DisplayDelegate)Delegate.Combine(del, del1);
foreach (DisplayDelegate minf in del.GetInvocationList())
{
Console.WriteLine(minf.Method.Name);
}
}
Post a Comment