Posted by: jaimalchohan on: April 26, 2008
Asynchronous Programming Part 1 – Delegates
Asynchronous Programming is the ability to perform multiple operations at the same time. For example, connect to a web service whilst at the same time connecting to a database and still responding to user input (avoiding a ‘frozen’ window).
To understand and be able to use Asynchronous programming techniques there are a couple of .Net features that need to be covered first, and this 3 part blog will provide you will all the knowledge you need to assess when and understand how to implement Asynchronous code.
At the core of Async is a feature known as delegates.
Delegates have been around since .Net 1. You’re using delegates every time you build any application with a UI, and behind the scenes Visual Studio .Net implements the delegates on your behalf.
To wire a button to an event, in your client side .aspx you’d usually write the following:
And in your server side .cs:
protected void btnOne_Click(object sender, EventArgs e)
{
// do work
}
But how does .Net actually call the btnOne_Click method?
Event Handlers = Delegate
In .Net 1.0 you had to explicitly define Event Handlers, in .Net 2.0 this is all performed at runtime during page initialization. What .Net 2.0 actually does during page initialization is create and run the following:
protected void Page_Init(object sender, EventArgs e)
{
btnOne.Click += new EventHandler(btnOne_Click);
}
As you can see, I’ve done this in the Page_Init event, and I’ve had to append to the btnOne.Click event an EventHandler class, which takes the btnOne_Click method as its parameter.
The EventHandler class is a special type of class whose sole purpose it is to wrap up calls to another method.
By using the EventHandler above, we can assign as many methods as we like to the btnOne.Click event without btnOne.Click having to know the names of our methods. Then when btnOne is clicked it can execute each of those methods.
protected void Page_Init(object sender, EventArgs e)
{
//All the 3 below do eactly the same thing
EventHandler handler = new EventHandler(btnOne_Click_1);
btnOne.Click += handler;
btnOne.Click += new EventHandler(btnOne_Click_2);
btnOne.Click += btnOne_Click_3;
//Events are similar to arrays, we can remove delagates from them
btnOne_Click -= new EventHandler(btnOne_Click_1);
}
Note: you can’t depend on the delegates to be invoked in any specific order
If delegates are a special type of class, then surly you can create your own? Absolutely. A delegate is basically a method signature (no code body, just the name, parameters and return value of a method). Also it’s good practice to include the word ‘Handler’ as a name suffix so that other coders can immediately tell that it’s a delegate.
public partial class WebForm1 : System.Web.UI.Page
{
delegate int MyCustomHandler(int a, int b);
The delegate above requires that any method to be wrapped up by MyCustomHandler must return an int and must have 2 int’s as it’s parameters.
You can then define a method, use the delegate to wrap up the method and call the invoke method on the delegate to execute the wrapped up method.
delegate int MyCustomHandler(int a, int b);
protected int DoWork(int a, int b)
{
return a + b;
}
protected void PageLoad(object sender, EventArgs e)
{
MyCustomHandler = new MyCustomHandler(DoWork);
ExecuteDelegate(handler);
}
protected void ExecuteDelegate(MyCustomHandler handler)
{
int myInt = handler.Invoke(3, 7);
// myInt = 10
}
As you will have noticed, the delegate can be passed around and you can use anything you want as the parameters, however it’s standard practice to pass an object (the object which raised the event) and EventArgs (or a class deriving from it) as the delegates parameters.
public partial class WebForm1 : System.Web.UI.Page
{
delegate int MyCustomHandler(object sender, MathsArgs e);
protected void PageLoad(object sender, EventArgs e)
{
MyCustomHandler = new MyCustomHandler(DoWork);
ExecuteDelegate(handler);
}
protected void ExecuteDelegate(MyCustomHandler handler)
{
MathsArgs args = new MathsArgs(3, 7);
int myInt = handler.Invoke(this, args);
// myInt = 10
}
protected int DoWork(object sender, MathsArgs e)
{
// by allowing the calling object to be
// passed it as a paramater you can easily obtain
// a reference to it, very handy when working
// with asp.net controls
// WebForm1 form = (WebForm1)sender;
return e.A + e.B;
}
}
public class MathsArgs : EventArgs
{
public int A;
public int B;
public MathsArgs(int a, int b)
{
A = a;
B = b;
}
}
Finally a quick look at events, an event basically has the job of containing and calling the invoke method on multiple delegates. Using delegates you subscribe to the event, and when the event is raised (by calling Invoke) the delegates have their Invoke methods called.
It’s important to check that the event actually contains a delegate, else the event will be a null object and attempting to invoke it will throw an error.
namespace DelegatesAndEvents
{
// You would probably store this is in an assembly which could be used by
// other applications
public class UserController
{
// declare a delegate which will define the signature
// of the methods which can subscribe to the event
public delegate void LoginHandler(object sender, EventArgs e);
//declare an event and define the type of delagte it allows
public event LoginHandler LoginEvent;
protected void Login(object sender, EventArgs e)
{
//Log the user into the system
//Then invoke the Login Event
if (LoginEvent != null)
LoginEvent.Invoke(this, new EventArgs());
}
}
public class WebForm1 : Page
{
UserController userController;
public void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
userController = new UserController();
userController.LoginEvent += new UserController.LoginHandler(SendEmail);
userController.LoginEvent += new UserController.LoginHandler(LogtoDB);
}
}
protected void SendEmail(object sender, EventArgs e)
{
// send an email alert informing people that somebody
// has logged in
}
protected void LogtoDB(object sender, EventArgs e)
{
// write data to the database becuase somebody has logged in
}
}
}
Note: the C# compiler allows you to leave the Invoke method, so you could call the above by calling MyCustomEvent(this, new EventArgs())
That concludes Asynchronous programming Part 1; in Part 2 I will be covering Threading.