Click here to Skip to main content
Click here to Skip to main content

How Events Work Under the Surface

By , 1 Jan 2007
 

Introduction

This article aims to show you what goes on behind the "magic" that the compiler does when you declare a simple event member in a class. It is meant as a beginner article to answer a frequently-asked question, so please don't vote it down just because it is very basic.


Multicast Delegates: The Foundation of Events

A pre-requisite to understanding how events work under the surface is to understand how multicast delegates work. A multicast delegate is a combined delegate that is made up of multiple single delegates joined together into a single object. The delegate can be called the same way you would call a normal delegate that only represents a reference to one method, but it will invoke all the methods it represents each time it is called. Let's see this in action. Say we have 2 methods in a class, named MyMethod1 and MyMethod2:

public void MyMethod1()
{
    Console.WriteLine("Method 1");
}

public void MyMethod2()
{
    Console.WriteLine("Method 2");
}

The methods have no parameters or return value so we create an "empty" delegate signature with no return value or arguments:

public delegate void EmptyDelegate();

Now, we want to call both delegates at once, so we use Delegate.Combine() to create a combined delegate, with references to both methods:

//Create a new multicast delegate that contains references to 
//both methods at once.
EmptyDelegate multicast=(EmptyDelegate)Delegate.Combine(
    new EmptyDelegate(MyMethod1),
    new EmptyDelegate(MyMethod2)
    );
//Call the new multicast delegate.
multicast();

When the multicast delegate is called, both methods will be executed in the order they were combined in, so the console output will be:

Method 1
Method 2

How Events Are Implemented

Now that we've seen how multi-cast delegates work, let's look at how events make use of them. An event is a wrapper over a multi-cast delegate field, that only allows outside objects to add and remove handlers. To better understand how this works, let's look at the more verbose way to declare an event.

First, define a field whose data type is a delegate type (in this case EventHandler):

private EventHandler myEventHandlers;

This is a simple field that can hold a multi-cast delegate containing references to all the handlers that are listening to the event.

Next we define a special event declaration, that is much like a property in its syntax, but it is declared with the event keyword, instead of get and set accessors, to get and set a property value, it has add and remove accessors, to add and remove handlers from the event. Notice the use of Delegate.Combine() to combine delegates, and Delegate.Remove() to remove a single delegate from a multi-cast delegate.

//Event declaration - similar in syntax to a property declaration
public event EventHandler MyEvent
{
    add
    {
        //Combine the multi-cast delegate that has the existing handlers
        //with the new handler delegate, to make a new multi-cast delegate 
        //that includes both the existing handlers and the new handler.
        myEventHandlers = (EventHandler)
            Delegate.Combine(myEventHandlers, value);
    }
    remove
    {
        //Create a new multi-cast delegate that contains all 
        //the existing handlers,
        //except for the one that's being removed.
        myEventHandlers = (EventHandler)
            Delegate.Remove(myEventHandlers, value);
    }
}

This will result in the same generated code as the normal type of declaration will:

public event EventHandler MyEvent;

So there you go: underneath, an event is a multi-cast delegate in a private field (member variable), and then a special public accessor that only allows adding and removing handlers from the multi-cast delegate.

License

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

About the Author

J. Dunlap
Web Developer
United States United States
My main goal as a developer is to improve the way software is designed, and how it interacts with the user. I like designing software best, but I also like coding and documentation. I especially like to work with user interfaces and graphics.
 
I have extensive knowledge of the .NET Framework, and like to delve into its internals. I specialize in working with VG.net and MyXaml. I also like to work with ASP.NET, AJAX, and DHTML.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionevent or thread,which one is more efficient ? ?memberokcode7-Aug-11 13:12 
use event, or use task-list and then use thread to check the list and processes,
which one is more efficient ?
 
those processes are not CPU-heavry but a lot od small tasks.
QuestionHelp Pleasememberredcat2220-Jan-07 8:32 
Hi
 
Let say that I create a contol with one Panel and in the midle of this panel i put a groupBox (smaller than the Panel).
 
What I need is to implemente a event (MouseMove) that if the mouse is in the Panel I can get the mouse position and if the mouse is over the groupBox I can get to the mouse positon in panel coordinates.
 
I know that I can do this using the mousemove event of the groupBox and the mousemove of the Panel. But What I realy need is to have just one event that is trigered for this two controles (panel and groupBox).How can this be achived?
 
thanks
 

 

 

AnswerRe: Help Pleasememberrdkerr31-Jan-07 19:08 
Hey,
 
One way is to add the offset of the location of the GroupBox in the Panel to the X and Y co-ordinates if the mouse is is in the GroupBox. I created a new method with the same signature of a standard MouseEventHandler, then attached it to both the Panel and GroupBox MouseMove events.
 

Not sure if this is what you are looking for but here is some quick and dirty code:
 
 
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ZoneMouseMove);
            this.groupBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ZoneMouseMove);
        }
 

        private void ZoneMouseMove(object sender, MouseEventArgs e)
        {
            int XPanel;
            int YPanel;
 
            if (sender is System.Windows.Forms.GroupBox)
            {
              XPanel = e.X + groupBox1.Location.X;
              YPanel = e.Y + groupBox1.Location.Y;
            }
            else
            {
              XPanel = e.X;
              YPanel = e.Y;
            }
 

            // Show X & Y position relative to Panel.
            textBox1.Text = XPanel.ToString();
            textBox2.Text = YPanel.ToString();
 
        }
        
    }

 
Hope this helps,
 
Ryan
General.NET 2.0 makes it a bit easiermemberDaniel Ruehmer8-Jan-07 22:08 
Very nice article!
 
Speaking of simplicity I just want to hint .NET 2.0 users to the fact that there's a template class to define event handlers. It looks like this:
public event EventHandler<EventArgs> MyEvent;
Now you don't need to declare your own delegate and it works even with custom event-arguments classes (replacing the EventArgs statement by a custom event-arguments class).
 
Daniel Rühmer
Application engineer for Measurement Software

GeneralExceptionsmvpJosh Smith8-Jan-07 11:56 
Nice article. Got my 5. Smile | :)
 
Since you mentioned how an event is actually a multicast delegate, you might want to consider mentioning when you would decide to use explicitly use a multicast delegate instead of an event. One common situation for that is if all of the event handlers must be invoked. When an event is raised, if one of the callbacks (event handling methods) throws an unhandled exception, then none of the other callbacks are invoked.
 
If you must ensure that all callbacks are invoked, you can get the list of callbacks from the MulticastDelegate and invoke them yourself. In that scenario you'd put each callback invocation in a try/catch block so that an exception does not percolate up the callstack. One drawback of that situation is that the code which "raises the event" (i.e. requests the callbacks to be invoked) will not be able to handle the exception.
 

 
:josh:
My WPF Blog[^]

We are what we repeatedly do. Excellence then, is not an act, but a habit. - Aristotle

GeneralAnother using of eventsmemberErtan Tike8-Jan-07 8:00 
You may use also
 
public event EventHandler MyEvent
{
add
{
myEventHandlers += value;
}
remove
{
myEventHandlers -= value;
}
}
 
same thing but more clear.
GeneralRe: Another using of eventsmembertlongman8-Jan-07 19:49 
Hi,
 
Not "same thing but more clear." to some of us. I had no idea that
+= and -= were really using delegate.combine and delegate.remove until
this article.
 
Thanks for a helpful article,Smile | :)
Tom
GeneralNice refresher...member0xfded3-Jan-07 3:29 
Most of us who've used C# from the start have dug into this before; however it's easy to forget the details in time.
 
Nice development of the concepts without an excessive amount of material.
 
Thanks for the refresher.
QuestionLocking???memberJames Ivie3-Jan-07 2:47 
You're missing one thing--locking. If add (or remove) is called on two threads simultaneously, both adds must occur. The way you have it written, they might not. I'm not sure exactly how MS does the locking, but they definitely do some kind of locking.
AnswerRe: Locking???memberJ. Dunlap3-Jan-07 3:00 
What the compiler does in normal cases is add:
[MethodImpl(MethodImplOptions.Synchronized)]
...as an attribute on the add and remove methods. This tells the compiler to make sure that access to the method is always done in a synchronized way (the compiler adds the "synchronized" modifier the the generated IL bytecode, which tells the JIT to emit synchronization code when it is compiled to machine code). Maybe I should mention this in the article, but I didn't want to potentially confuse people by giving them more info at a time than they are ready for. What do you think?

 

GeneralRe: Locking???memberJames Ivie3-Jan-07 11:00 
Yes, I just wanted to make sure that there was a clarification somewhere on this page that replacing the implied "add/remove" code for an event with the code above will likely cause problems when multiple threads are used. The statement above that: "This will result in the same generated code as the normal type of declaration will" is not accurate. Either the attribute you mention or the equivalent "lock (this)" block would have to be added to make that statement true.
GeneralDelegatesmemberSpiff Dog2-Jan-07 9:13 
Howdy!
 
Since this is a beginners guide, you can't assume that the reader will know, or perhaps, comprehend what a delegate is. You may consider defining what a delegate and an event are before diving into the meat of the subject.
 
A good overview on delegates can be found at O'Reilly's[^] site.
 
-- Spiff
 
-------------------------

Spiffdog Design

It's ok.. he's no ordinary dog...

GeneralRe: DelegatesmemberJ. Dunlap3-Jan-07 3:24 
I considered discussing delegates, but I wanted the article to be simple and focused. It's kind of a hard decision, what to leave out and what to put in. Maybe I'll just link to that article or another similar one.

 

GeneralExtra infomemberleppie1-Jan-07 21:56 
Events are more limited than delegates in the sense they can only be invoked from the declaring class, for all other purposes, they are exactly the same :p
 

GeneralRe: Extra infomemberJ. Dunlap3-Jan-07 3:20 
leppie wrote:
Events are more limited than delegates in the sense they can only be invoked from the declaring class, for all other purposes, they are exactly the same :p

When you access the event from the declaring class, the compiler generates code that accesses the event's backing field directly. From outside the declaring class, the only thing you can do is add or remove a handler, using += and -=, which delegate to the add and remove methods as described in my article.

 

GeneralRe: Extra infomemberrdkerr22-Jan-07 12:24 
Can't events be included in the definition of an Interface, where delegates cannot?
 
Ryan
GeneralRe: Extra infomemberleppie22-Jan-07 18:22 
Thats true! Kinda forgot about that :p
 

GeneralVery good and solid articlememberMartin#1-Jan-07 19:38 
Thanks for taking time to do this article.
I think there will be a lot of links in future from c# forum to this article.
 
All the best,
 
Martin
GeneralRe: Very good and solid articlememberJ. Dunlap3-Jan-07 3:20 
Thanks! Smile | :)

 

Generalthanks for the article...membersubai1-Jan-07 19:09 
thank you for your article .
i think events in asp are more confusing Smile | :)
 
I Wish the Life Had CTRL-Z

GeneralRe: thanks for the article...memberJ. Dunlap3-Jan-07 3:20 
Thanks! Smile | :)

 

GeneralGood idea - well done.memberPete O'Hanlon1-Jan-07 10:05 
I've just come across from the message board to vote on this article. Well done.Big Grin | :-D
 

the last thing I want to see is some pasty-faced geek with skin so pale that it's almost translucent trying to bump parts with a partner - John Simmons / outlaw programmer

Deja View - the feeling that you've seen this post before.

GeneralRe: Good idea - well done.memberJ. Dunlap1-Jan-07 12:12 
Thanks! Smile | :)

 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 1 Jan 2007
Article Copyright 2007 by J. Dunlap
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid