Events and Delegates in C#
Written by Thong D. Nguyen Friday, 02 July 2010 09:07
Blog - Programming
When I was trying to learn events and delegates, I read a lot of articles to completely understand what they are and how to use them, and now I want to present them all here, everything I learned, most of the things you need to learn.
Delegate and Event concepts are completely tied together. Delegates are just function pointers, That is, they hold references to functions.
A Delegate is a class. When you create an instance of it, you pass in the function name (as a parameter for the delegate's constructor) to which this delegate will refer.
Every delegate has a signature. For example:
Delegate int SomeDelegate(string s, bool b);
is a delegate declaration. When I say this delegate has a signature, I mean that it returns an int type and takes two parameters of type string and bool.
I said, when you instantiate delegates, you pass in the function name to which this delegate will refer as its constructor parameter. The important thing to notice is that only functions that have the same signature as the delegate, can be passed as a parameter.
Consider the following function:
private int SomeFunction(string str, bool bln){...}
You can pass this function to SomeDelegate's constructor, because of their similar signatures.
SomeDelegate sd = new SomeDelegate(SomeFunction);
Now, sd refers to SomeFunction, in other words, SomeFunction is registered to sd. If you call sd, SomeFunction will be invoked. Keep in mind what I mean by registered functions. Later, we will refer to registered functions.
sd("somestring", true);
Now that you know how to use delegates, let's understand events...
- A
Buttonis a class, when you click on it, theclickevent fires. - A
Timeris a class, every millisecond atickevent fires.
Want to understand what's happening? Let's learn through an example:
This is the scenario: we have a class named
Counter. This class has a method namedCountTo(int countTo, int reachableNum)which starts counting from 0 tocountTo, and raises an event namedNumberReachedwhenever it reaches thereachableNum.
Our class has an event: NumberReached. Events are variables of type delegates. I mean, if you want to declare an event, you just declare a variable of type some delegate and put event keyword before your declaration, like this:
public event NumberReachedEventHandler NumberReached;
In the above declaration, NumberReachedEventHandler is just a delegate. Maybe it was better to say: NumberReachedDelegate, but notice that Microsoft doesn't say MouseDelegate or PaintDelegate, instead it offers: MouseEventHandler and PaintEventHandler. It's a convention to say NumberReachedEventHandler instead of NumberReachedDelegate. OK? Good!
You see, before we declare our event, we need to define our delegate (our event handler). It could be something like this:
public delegate void NumberReachedEventHandler(object sender,
NumberReachedEventArgs e);
As you see, our delegate's name is: NumberReachedEventHandler, and its signature contains a void return value and two parameters of type object and NumberReachedEventArgs. If you somewhere want to instantiate this delegate, the function passed in as constructor parameter should have the same signature as this delegate.
Have you ever used PaintEventArgs or MouseEventArgs in your code to determine the position of the mouse, where it was moving, or the Graphics property of the object which raised the Paint event? Actually, we provide our data for the user in a class which is derived from EventArgs class. For example, in our example, we want to provide the number which was reached. And here is the class definition:
public class NumberReachedEventArgs : EventArgs
{
private int _reached;
public NumberReachedEventArgs(int num)
{
this._reached = num;
}
public int ReachedNumber
{
get
{
return _reached;
}
}
}
If it wouldn't be necessary to provide the user with any information, we just use the EventArgs class.
Now, every thing is prepared to take a look inside our Counter class:
namespace Events
{
public delegate void NumberReachedEventHandler(object sender,
NumberReachedEventArgs e);
/// <summary>
/// Summary description for Counter.
/// </summary>
public class Counter
{
public event NumberReachedEventHandler NumberReached;
public Counter()
{
//
// TODO: Add constructor logic here
//
}
public void CountTo(int countTo, int reachableNum)
{
if(countTo < reachableNum)
throw new ArgumentException(
"reachableNum should be less than countTo");
for(int ctr=0;ctr<=countTo;ctr++)
{
if(ctr == reachableNum)
{
NumberReachedEventArgs e = new NumberReachedEventArgs(
reachableNum);
OnNumberReached(e);
return;//don't count any more
}
}
}
protected virtual void OnNumberReached(NumberReachedEventArgs e)
{
if(NumberReached != null)
{
NumberReached(this, e);//Raise the event
}
}
}
In the above code, we raise an event if we reach the desired number. There are a lot of things to consider here:
- Raising an event is accomplished through calling our event (an instance of some delegate named
NumberReachedEventHandler):NumberReached(this, e);This way, all registered functions will be invoked.
- We prepare data for registered functions through this:
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum); - One question: why do we indirectly call
NumberReached(this, e)throughOnNumberReached(NumberReachedEventArgs e)method? Why didn't we use the following code?if(ctr == reachableNum)
{
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
//OnNumberReached(e);
if(NumberReached != null)
{
NumberReached(this, e);//Raise the event
}
return;//don't count any more
}Good question! If you want to know why we called indirectly, take another look at
OnNumberReached's signature:protected virtual void OnNumberReached(NumberReachedEventArgs e)
- You see, this method is
protected, it means it's available for classes which are derived from this class (inheriting classes). - This method is also
virtual, this means that it could be overridden in a derived class.
And this is very useful. Suppose you are designing a class which inherits from
Counterclass. By overridingOnNumberReachedmethod, you can do some additional work in your class before the event gets raised. An example:protected override void OnNumberReached(NumberReachedEventArgs e)
{
//Do additional work
base.OnNumberReached(e);
}Note that if you don't call
base.OnNumberReached(e), the event will never be raised! This might be useful when you are inheriting from some class and want to eliminate some of its events! An interesting trick, huh?As a real world example, you can just create a new ASP.NET Web Application and take a look inside the code behind generated. As you see, your page inherits from
System.Web.UI.Pageclass. This class has avirtualandprotectedmethod nameOnInit. You see thatInitializeComponent()method is called inside the overridden method as an extra work, and thenOnInit(e)is called in the base class:#region Web Form Designer generated code
protected override void OnInit(EventArgs e)
{
//CODEGEN: This call is required by the ASP.NET Web Form Designer.
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion - You see, this method is
- Notice that
NumberReachedEventHandlerdelegate is defined outside our class, inside the namespace, visible to all classes.
OK. Now, it's time to practically use our Counter class:
In our sample application, we have two textboxes named txtCountTo and txtReachable as follows:

And here is the event handler for btnRun click event:
private void cmdRun_Click(object sender, System.EventArgs e)
{
if(txtCountTo.Text == "" || txtReachable.Text=="")
return;
oCounter = new Counter();
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached);
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text),
Convert.ToInt32(txtReachable.Text));
}
private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
{
MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
}
This is the syntax for initiating an event handler for some event:
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached);
Now, you understand what you are doing here! You are just instantiating an object of type NumberReachedEventHandler delegate (as you do for any other object). Care about the oCounter_NumberReached method signature similarity as I mentioned before.
And notice that we used += instead of simply =.
It's because delegates are special objects that can hold references to more than one object (here, reference to more than one function). For example, if you had another function named oCounter_NumberReached2 with the same signature as oCounter_NumberReached, both of the functions could be referenced this way:
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached);
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached2);
Now, after raising an event, both functions will be invoked one after another.
If somewhere in your code, based on conditions, you decided oCounter_NumberReached2 not be invoked anymore on NumberReached event occurrences, you could simply do this:
oCounter.NumberReached -= new NumberReachedEventHandler(
oCounter_NumberReached2);
A lot of folks keep asking this question: "what happens if we don't use event keyword?"
In essence, declaring the event keyword prevents any of the delegate’s users from setting it to null. Why is this important? Imagine that as a client I would add to the delegates invocation list a callback to one of my class’ functions. So would other clients. All is well and good. Now imagine that someone, instead of using the “+=”, is simply setting the delegate to a new callback by using “=”. This basically just throws the old delegate and its invocation list down the drain and creates a whole new delegate with a single item in its invocation list. All the other clients will not receive their callbacks when the time comes. It is this kind of situation that having the event keyword is aiming to solve. If I remove the event keyword in the Counter class and try to compile the following code in my application, it will cause a compilation error:
In conclusion: an event declaration adds a layer of protection on the delegate instance. This protection prevents clients of the delegate from resetting the delegate and its invocation list, and only allows adding or removing targets from the invocation list [reference].
Don't forget to define these lines in your application's main constructor, instead of cmdRun_Click event handler. I defined them in my button click event handler just for the sake of simplicity! ;-)
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
oCounter = new Counter();
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached);
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached2);
The source code provided by this article is as above.
If your vote is less than 5, let me know the reason ;-). I hope you will be satisfied after reading this article!
- Thursday, August 21, 2003
- The article has a better decorated coding.
- Some image of demo application added.
- Wednesday, August 11, 2004
- Added an example of inheritance.
- Saturday, March 12, 2005
- More about the usage of
eventkeyword.
- More about the usage of
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here
Maysam Mahfouzi![]() (Source: CodeProject.com) |
|
| < Prev | Next > |
|---|
Idioms
- Habit cures habit. (Lấy độc trị độc)
- Don't count your chickens, before they are hatched. (Chưa đỗ ông Nghè đã đe Hàng tổng)
Who's online
Location
38.107.179.220
38.107.179.220Search Bot
unknown unkno

OS
PHP
MySQL
Time
Caching
GZIP
Members
Content
Web Links





Today
Yesterday
This week
Last week
This month
Last month
All days

