Friday, December 28, 2007

Delegates and Events in C#

Delegates and Events simplified in C#

.NET framework facilitates callbacks in a much safer and more object-oriented manner using delegates.
A delegate is a type-safe object that points to another method (or possibly multiple methods) in the pplication, which can be invoked at a later time.

.NET delegates can point to either static or instance methods.

Defining a delegate in C#

public delegate int MathOp(int x, int y);

when c# compiler processes delegate types, it autometically generates a sealed class derived
from System.MulticastDelegate.

sealed class MathOp: System.MulticastDelegate
{
constructor(object target, unit functionaddress)
int Invoke(int x, int y);
BeginInvoke(int x, int y, ASynCallback cb, object state);
public int EndInvoke(IAsyncResult result);
}



Delegate Example:
namespace Simple
{

public delegate int MathOp(int x, int y);

public class SimpleMath
{

public static int Add(int x, int y)
{ return x+y;}

public static int Subtract(int x, int y)
{return x-y;}
}

class Program
{
static void Main()
{
MathOp m= new MathOp(SimpleMath.Add);

call Add() method using delegate
m(20,10); // invoke() gets called, invoke calls Add()
}
}

a delegate type (sealed class) maintains following important things
  • the name of the method on which it makes calls
  • the arguments (if any) of this method
  • the return value (if any) of this method

MathOp has same signature as of Add() and Sub(). It means MathOp object (delegate) can't hold address of any method with different signature hence its type safe.

Also, .NET delegates are automatically powered with the ability to call their methods synchronously ( Invoke() ) and asynchronously (BeginInvoke()) , in turn, it greatly simplifies programming tasks.

Enabling Multicasting:
a delegate object can maintain a list of methods to call, rather than a single method. simply make
use of overloaded += operator in such a scenario.

MathOp m=null;
m += new MathOp(SimpleMath.Sub);
m += new MathOp(SimpleMath.Add);
m(30, 10); // first Add() and then Sub() gets called.

C# Events:
Consider a class bike which raises the event when speed of bike reaches 100kmph and bike object communicates with client program. Client program must register it's method with bike
so that bike object can communicate with client program. Assume the client method's signature is as void Methodname(string str);

namespace Example
{

class bike
{
public delegate void BikeSpeed(string str); //BikeSpeed is declared within bike. (nested class)
private int speed;
private BikeSpeed overspeed;

public void accelerate(int s)
{
speed=s;
if(speed==100)
overspeed("it's rash driving"); // call event handler through delegate.
}

public void register(BikeSpeed bs)
{
overspeed=bs; // Client's method (RashDriving) address is assigned to overspeed
}
}

class Client
{
static void Main()
{
bike b1= new bike(); // creates bike object on client program side.
b1.register(new bike.BikeSpeed(RashDriving)); // register the method with bike, here bike object will communicate in case of speed==100.

for(int i=0; i<6;i++)>

Console.ReadLine();

}

public static void RashDriving(string str)

{

Console.WriteLine(str);

}

}

}

} In above example, client program register the RashDriving( ) method with bike object, by calling register( ) of bike class.

When speed of bike reaches 100, through overspeed delegate, RashDriving() is called back. But, as far as writing event handler is concrened, microsoft has a recommonded pattern for the same as given below.

void MethodName( object sender, ClassDerivedFromEventArgs e);
where ClassDerivedFromEventArgs is derived from System.EventArgs class.
The System.EventArgs is the base class for classes containing event data.

Also, in above example, you need to explicitly register the client's method with bike object.
Now, Let us re-write the above program to handle bike event, considering above points. namespace Example
{
class BikeEventArgs: EventArgs
{
public int speed;
public string msg;

public BikeEventArgs(int s, string m)
{
speed=s; msg=m;
}
}

class bike
{
public delegate void BikeSpeed(object sender, BikeEventArgs e); //BikeSpeed is declared within bike. (nested class)
private int speed;
public event BikeSpeed overspeed;
// by processing event keyword C# compiler adds two methods which are
// add_xxx(); and remove_xxx() which are used for registration as well as
// as unregistring event handlers. here in our case add_overspeed() and remove_overspeed()

public void accelerate(int s)
{
speed=s;
if(speed==100)
overspeed(this, new BikeEventArgs(speed, "it's rash driving"));
}

}

class Client
{
static void Main()
{
bike b1= new bike(); // creates bike object on client program side.
b1.overspeed+= new bike.BikeSpeed(RashDriving); // '+=' triggers add_overspeed()

for(int i=0; i<6;i++)

b1.accelerate(20);

Console.ReadLine();

}

public static void RashDriving(object sender, BikeEventArgs e)

{

Console.WriteLine("important message from {0}: {1}, at the speed {2}",sender,e.msg, e.speed);

}

} }

In above program:

1) You are registering client's method through += operator, which calls actually add_overspeed().

2) Event handler method has recommonded event pattern signature of void Methodname(sender object, BikeEventArgs e);

This is how through delegates events are handled in a consistent way in .NET.