Delegates And Events - The Uncensored Story
http://www.csharpfriends.com
World's Greatest C# Community    
Home Articles C# Forums Books C# Syntax C# Spec C# Jobs free Source Code Advertise About
 

Control Panel

[ Sign In / register ]
Points   
Notes 
My Forums
My Tutorials
My Profile

Resources

Learn
 Articles
 QuickStarts
 C# Spec
 Whitepapers
 Tools
 Class Browser
 C# Code Generator
 Links
 Misc Rss Feeds
 Code Highlight
 411 Directory
 FREE magazines
 freevb.net

Reviews
  ASP.NET Hosting

Source Code
 Get Version 1.0



C# Consulting
AspDotNetStoreFront
Chapter:   UnCategorized
Current Lesson:
Delegates And Events - The Uncensored Story
[Latest Content]
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | ALL
[prev. Lesson]  Hello World - Console Application [next Lesson]  Delegates And Events - The Uncensored Story (page 2)
Delegates And Events - The Uncensored Story
  by: Salman Ahmed

Delegates And Events - The Uncensored Story - Part 1
By A. Abdul Azeez


Introduction

All of us have been exposed to event driven programming of some sort or the other. C# adds on value to the often mentioned world of event driven programming by adding support through events and delegates. This article is a part of a series that aims at understanding fully the way in which delegates and events operate. Part1 helps you understand the role of multicast delegates in the context of UI interaction. The emphasis of this article would be to identify what exactly happens when you add an event handler to your common UI controls. A simple simulation of what could possibly be going on behind the scenes when the AddOnClick or any similar event is added to the Button class will be explained. This will help you understand better the nature of event handling using multi cast delegates. All the examples provided are in C#.

Events

An event is the outcome of an action. There are two important terms with respect to events. The event source and the event receiver. The object that raises the event is called event source and the object that responds to the event is called event receiver. Did you observe a hitch in the above explanation? O.K. my event source raises the event and my event receiver receives it, but what is the communication channel between them. Take the instance of you talking with a friend of yours over the telephone. Now you are the event source and your friend is the event receiver and the telephone cable is the communication channel. Similarly the communication channel between an event source and an event receiver is the delegate. The internals of events and their relationship to delegates would be dealt with in a later article. The explanation of events here is just to help the reader understand and visualize what exactly events are.

Delegates

Forget computers for a moment. Just think about delegates in the real world. Who are delegates? Delegates are people who represent a particular country or region or state in another country or region or state. Extend this same definition now to C#. In C#, delegates act as an intermediary between an event source and an event destination.

To be even precise delegates are similar to function pointers. They can be called as type safe function pointers. Delegates have other uses in addition to event handling. But this article would focus on describing delegates with respect to event handling. A delegate class used as an intermediary between an event source and event receiver is called an event handler.

Types of Delegates

There are basically two types of delegates. Single Cast delegate and Multi Cast delegate. Let us first understand at the broader level the definition of both these delegates. A single cast delegate can call only one function. A multi cast delegate is one that can be part of a linked list. The multi cast delegate points to the head of such a linked list. This means that when the multi cast delegate is invoked it can call all the functions that form a part of the linked list. Assume that i have several clients who would like to receive notification when a particular event occurs. Putting all of them in a multi cast delegate can help call all the clients when a particular event occurs.

To support a single cast delegate the base class library includes a special class type called System.Delegate. To support multi cast delegates the base class library includes a special class type called SystemMultiCastDelegate.

Single Cast delegate

The signature of a single cast delegate is shown below. The letters in italics can be replaced with your own names and parameters.

public delegate Boolean  DelegateName (parm1, parm2)
When the compiler compiles the statement above, it internally generates a new class type. This class is called DelegateName and derives from System.Delegate. Just to make sure just check the ILDisassembler code to check that this is happening. As an example let us create a single cast delegate named MyDelegate which would point to a function MyFunction. The code appears as below,
public delegate Boolean MyDelegate(Object sendingobj, Int32 x);

public class TestDelegateClass
{     
    Boolean MyFunction(Object sendingobj, Int32 x) 
    {           
        //Perform some processing           
        return (true);  
    }
           
    public static void main(String [] args)
    {
        //Instantiate the delegate passing the method to invoke in its constructor
        MyDelegate mdg = new MyDelegate(MyFunction);
            
        // Construct an instance of this class
        TestDelegateClass tdc = new TestDelegateClass();
               
        // The following line will call MyFunction
        Boolean f = mdg(this, 1);
       
    }
}

How does the above code work? The System.Delegate contains a few fields. The most important of them are Target and Method. The Target field identifies an object context. In the scenario above this would point to the TestDelegateClass being created. If the method to be invoked is a static method then this field will be null. The Method field identifies the method to be called. This field always has some value. It is never null. To the intelligent reader, if you observe the IL Disassembler code you would see that the constructor for MyDelegate contains two parameters. But in our case we are passing only one parameter? Any guesses on how this is done? Yeah you are right! It gets done internally by the compiler. The compiler resolves the call above to a call passing in the two parameters required. If you observe in C++ with managed extension, the object and the address of the function have to be passed explicitly. But in VB and C# we have done away with this with the compiler filling in the necessary details. In the code sample above we saw,
Boolean f = mdg(this, 1);
When the compiler comes across this line, this is what happens, The compiler identifies that mdg is a delegate object. The delegate object has the target and method fields as described above. The compiler generates code that calls the Invoke method of the System.Delegate derived class i.e MyDelegate. The Invoke method internally uses the MethodInfo type identified by the delegate's Method field and calls the MethoidInfo's invoke method. The MethodInfo's invoke method is passed the delegates Target field which identifies the method and an array of variants which contains the parameters to the method to be called. In our case, the array of variants would be an Object and an Int32 value. The above discussion would have made you clear about what happens internally when a method gets called through a Single Cast delegate. Multi Cast delegate The signature of a mutli cast delegate is shown below. The letters in italics can be replaced with your own names and parameters.
public delegate void DelegateName (parm1, parm2)
Whatever has been explained with respect to Single cast delegate holds good even in the context of a Multi Cast Delegate. There is a small addition here. Since a multi cast delegate represents a linked list, there is an additional field called prev which refers to another Multi Cast Delegate. This is how the linked list is maintained. The return type if you have observed has changed from Boolean to void. Have you guessed the reason as to why it happens? The reason is that since several multi cast delegates get called consecutively we cannot wait to get the return value from each of these methods being called. The code shown in the example for single cast delegate will work in the case of multi cast delegate too. The only difference is that the delegate signature needs to be changed, the method signature needs to be changed to return void instead of Boolean. The power of multi cast delegates come in when you combine delegates to form a linked list. The Combine method is available in the System.Delegate class as a static method. The function signature as follows.
public static Delegate Combine(Delegate a, Delegate b);
What the above method does is that it combines the two delegates a and b and makes b's prev field point to a. It returns the head of the linked list. This in turn has to be type casted to our delegate type. Similar to the Combine method we have a remove method which removes a delegate from the linked list and gives you the modifies smaller linked list with a pointer to the head. The signature of the remove method is as follows,
public static Delegate Remove(Delegate source, Delegate value);
Here's a small sample that illustrates the use of a multi cast delegate,
using System;

class MCD1
{
    public void dispMCD1(string s)
    {
        Console.WriteLine("MCD1");
    }
}

class MCD2
{

    public void dispMCD2(string s)
    {
        Console.WriteLine("MCD2");
    }
}

 

public delegate void OnMsgArrived(string s);

class TestMultiCastUsingDelegates
{
    public static void Main(string [] args)
    {
        MCD1 mcd1=new MCD1();
        MCD2 mcd2=new MCD2();
        // Create a delegate to point to dispMCD1 of mcd1 object
        OnMsgArrived oma=new OnMsgArrived(mcd1.dispMCD1);

        // Create a delegate to point to dispMCD2 of mcd2 object
        OnMsgArrived omb=new OnMsgArrived(mcd2.dispMCD2);
 
        OnMsgArrived omc;
        // Combine the two created delegates. Now omc would point to the head of a linked list
        // of delegates
        omc=(OnMsgArrived)Delegate.Combine(oma,omb);
 
        Delegate [] omd;
         // Obtain the array of delegate references by invoking GetInvocationList()
        omd=omc.GetInvocationList();
 
         OnMsgArrived ome;
        // Now navigate through the array and call each delegate which in turn would call each of the
        // methods
        for(int i=0;i{
            // Now call each of the delegates
            ome=(OnMsgArrived)omd[i];
            ome("string");
        }
    }
}

The examples in this article would revolve around Multi Cast Delegates. It is essential that you have understood the basics of single cast and multi cast delegates before you proceed further. Problem Scenario The code below shows an example of adding a button to a Form. The button is associated with an event handler that handles whatever happens when the button is pressed. When the form is displayed and the button is pressed a message box showing "Button Clicked" will appear. Compilation directions: Compile using
csc /r:System.DLL;System.WinForms.DLL;Microsoft.Win32.Interop.DLL FormTest.cs
/*

Creating a form and adding a button to it and associating the button with an event handler
*/

using System;
using System.WinForms;
public class FormTest : Form
{
    //Create a button
    private Button button1 = new Button();
    public static void Main (string [] args)
    {
        //Run the application
        Application.Run(new FormTest());
    }
    
    public FormTest()
    {
        //Set up the Form
        this.Text = "Hello WinForms World";
        //Set up the button
        button1.Text = "Click Me!";
        //Register the event handler
        button1.AddOnClick(new System.EventHandler(buttonClicked));
        //Add the controls to the form
        this.Controls.Add(button1);
    }
 
    //The event handling method
    private void buttonClicked(object sender, EventArgs evArgs)
    {
        MessageBox.Show("Button clicked");
    }
}
The topic of our discussion would be the routing of calls between the time when the click event is raised and the time when the method displaying "Button clicked" is displayed. Components of the Exercise This section will talk about the components that would complete the architecture, 1. Delegate Event Handler A delegate event handler that routes the call to the appropriate method. Let us call it ButtonEventHandler. The declaration of this delegate should follow that the same for declaration of multicast delegates. The declaration is as follows.
public delegate void ButtonEventHandler(object sender, ButtonEventArgs e);
Parameters sender - Identifies the origin of the event ButtonEventArgs - Identifies the Event Arguments. This class is derived from EventArgs. EventArgs is the base class for event data. The data with respect to the event generated is going to be held in this class. The reason why I have made use of a seperate class is that i am going to use a string member variable in the derived class which identifies the the action. 2. EventArgs derived class The reason for using a derived class for EventArgs has been mentioned in the previous point. The class looks like this:
public class ButtonEventArgs : EventArgs
{      
    public string msg; //identifies the action
    public ButtonEventArgs(string message)
    {
        msg=message;  
    }
}
}
3. A button class This class is the simulation of the NGWS SDK Button Class. A control class such as a button class must be able to generate events when actions such as clicking on it are performed. Now think logically, the button is pressed and a message indicating the same should be displayed. The button here is the event source. Forget for a moment about the event receiver. Now what is required in between? Yeah! you guessed it right, a delegate to forward the calls to the appropriate event receiver. Now if, you want to add a delegate to your button you do it through Add methods in your button class. Similarly if you want to remove a delegate you do it through a Remove method in your button class. These methods must be named AddOn and RemoveOn according to the NGWSSDK documentation. Our button class will respond to click and press events. Thus the methods in our class should be AddOnPress, RemoveOnPress, AddOnClick and RemoveOnClick. The signature of the above methods will be as follows,
public void AddOnClick(ButtonEventHandler handler);
public void RemoveOnClick(ButtonEventHandler handler);
public void AddOnPress(ButtonEventHandler handler);
public void RemoveOnPress(ButtonEventHandler handler);

At the end of this we have added the methods which add and remove delegates corresponding to Click and Press events in the button class. Now what is required? The most important part, methods that raises the events. The naming conventions followed for such methods are On. Thus the methods in our class should be OnClick and OnPress. These methods are passed the event data. The method raises the events by invoking the delegates. The signature of the mehods will be as follows,
protected virtual void OnPress(ButtonEventArgs e);
protected virtual void OnClick(ButtonEventArgs e);
Why are the above two methods declared as protected? To help the subclasses override this event without attaching a delegate to it. What happens in the SDK Button class. When the button is pressed, an event data gets created, the OnClick method gets called passing in the event data as the parameter. The OnClick method in turn calls the event receivers through the delegates. Do you observe a missing link above? If the OnClick method gets passed the event data, who creates the event data? The answer is that the event data could be created by the SDK. For our purposes since we cannot tap onto the SDK let us simulate what the SDK does by creating two methods whose only purpose is to create the event data and call the appropriate OnClick or OnPress methods. The signature of these methods is as follows,
public void click();
public void press();
Now let us take a look at the whole button class, (page 2)

1 


Build Your Own ASP.NET Website Using C# & VB.NET

Chapter:  UnCategorized
Current Lesson:
Delegates And Events - The Uncensored Story
[Latest Content]
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | ALL
[prev. Lesson]  Hello World - Console Application [next Lesson]  Delegates And Events - The Uncensored Story (page 2)


Today's Top Movers
vulpes 6800
MadHatter 2220
jal 867
Jeff1203 857
muster 791

Yesterday Top Movers
shakti sin.. 9
MadHatter 3
C#fanatic 2
Al_Pennywo.. 2
garyweik1 1

Monthly Leaders
vulpes 6800
MadHatter 2260
jal 867
Jeff1203 857
muster 791

Top Members
mosessaur 18457
Rincewind 7074
stanleytan 6995
vulpes 6800
Gsuttie 6046

Great Offers
.net hosting
Go To My Pc
Remote Pc Control
zonealarm
spam blocker
web hosting directory
ad server   C#
snadtech GoToMyPc

Top of Page

Advertise | About | Link To Us | Privacy Notice Copyright © 2003 - 2005 CSharpFriends.com  All Rights Reserved  Visual C# Developer Center