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:
EmptyDelegate multicast=(EmptyDelegate)Delegate.Combine(
new EmptyDelegate(MyMethod1),
new EmptyDelegate(MyMethod2)
);
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.
public event EventHandler MyEvent
{
add
{
myEventHandlers = (EventHandler)
Delegate.Combine(myEventHandlers, value);
}
remove
{
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.