Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Understanding .NET Delegates and Events, By Practice

0.00/5 (No votes)
13 Jun 2008 2  
Truly understanding .NET delegates and events.

Contents

Introduction

This article is aimed at developers who deep down know that they do not really understand events and delegates even though they use them every day...

Many developers tend to use events and delegates "as is" without truly understanding the internal stuff - which, trust me, once understood, can help you do many cool stuff. When I first started .NET coding, I used to just double click the "Button" or "DataGrid" control, go to the code-behind, and write my code inside the event handler. Well, then a few years ago, I decided enough is enough, and I dug deep to really understand .NET events and delegates: This article is an attempt to share with you what I came up with.

This article tries to help developers “truly” understand .NET events and delegates by real examples. By illustrating the ideas presented via business examples, I will hopefully succeed in explaining the concept. So, let’s start…

Observer Pattern (Publish-Subscribe Pattern)

As a smooth start, let us understand delegates from a high level approach by comparing it to the Observer (also called the Publish-Subscribe) pattern:

The Observer pattern is one of the most well known patterns used in software engineering. This pattern states that a non-determined number of business entities – called subscribers – are interested in a change of state of another business entity – called publisher – and that the publisher does not care who these subscribers are or what and why they are concerned with its change of state.

For example, two business entities called “Employee” and “Manager” are interested to know if the business entity “Owner” encounters any bad change of state in health. Now, while the “Owner” does not really care or even know anything about who wants to know if he is sick, the “Employee” and “Manager” will definitely want to prepare get-well notes and flower packets for the beloved company “Owner”. Of course, they will have a hidden agenda behind that, but this is out of the scope of this article.

.NET uses Delegates between business entities to achieve this behavior.

Definition of Delegates

A delegate is a type safe “Function Pointer” which holds references to static or instance methods. Delegates are used to call methods dynamically at runtime (do not scratch your head; you will understand this by examples).

In .NET, a delegate is declared as follows:

public delegate int TestDelegate(int quantity);

The above line declares a delegate called “TestDelegate” which can dynamically call any method which has a return type of “int” and accepts a single parameter also of type “int”. This makes .NET delegates “type safe” in that they can only call methods that match their signatures.

Internally, the “delegate” keyword causes the compiler to generate a class that derives from System.MulticastDelegate, which is the base class for .NET delegates. The MulticastDelegate class, in turn, derives from the System.Delegate class. The MulticastDelegate class was an addition to the .NET Framework which first included only the Delegate class. The MulticastDelegate class added the ability of chaining a linked list of delegates called the “invocation list”. Translation: MulticastDelegate represents a delegate that can call more than one method.

OK, enough with the boring theory. Next, you will see examples that clear up the concepts discussed in this action and much more…

Trivial Delegates Example

Actually, this example is not that trivial. Although – as you will see – this example does not hold any real business value, it will actually give you a clear indication about how delegates work and how they can be used to call multiple methods through their invocation list.

Consider the code below:

public delegate void MyDelegate(string text);
protected void Page_Load(object sender, EventArgs e)
{
    MyDelegate d = new MyDelegate(MyMethod);
    //d += new MyDelegate(MyMethod1);
    d("hello");
}
private void MyMethod(string text)
{
    Response.Write(text);
}
//private void MyMethod1(string text)
//{
//    Response.Write("--" + text);
//}

Let us examine the example:

public delegate void MyDelegate(string text);

This line declares a delegate called “MyDelegate” which can call any method that has a return of type void and accepts a single string parameter.

MyDelegate d = new MyDelegate(MyMethod);

This line creates an instance of “MyDelegate” called “d” and adds a method “MyMethod” to its invocation list. This means that now delegate “d” will call the method “MyMethod” dynamically at runtime. How is this done? Keep going…

d(“hello”);

This line causes the delegate “d” to call the methods on its invocation list. In this case, the method “MyMethod” will be called. You can verify this by running the page, and you will see the value “hello” printed.

Now, let us see how delegates can be used to invoke multiple methods. For this, uncomment the commented lines.

d += new MyDelegate(MyMethod1);

This line adds a new method “MyMethod1” to delegate d's invocation list. This means that delegate “d” will now call methods “MyMethod” and “MyMethod1” because they are both on its invocation list (linked list). This time, if you run the code, you will see the value “hello--hello" printed as an indication that both methods were called at runtime.

Internal Delegates Representation

Delegates internally have two important properties: Target and Method. Target points to an object instance, while Method points to a method inside the object instance. Invoking a delegate means to call the method referenced in the Method property which belongs to the object instance referenced by the Target property. To clear things up, see how this explanation fits into the trivial sample given above:

1.JPG

This representation indicates that since in our code we added two methods “MyMethod” and “MyMethod1” to delegate’s d's invocation list, both methods are added to the data structure linked list. In both cases, the Target property is set to “this”, which is the page instance itself, while the Method property is set to the appropriate method name. Also notice how the order in which we added methods to the invocation list is apparent in the representation of the linked list: first, “MyMethod” is added where the Previous pointer is null, and then “MyMethod1” is added where the Previous pointer points to “MyMethod”.

Non-Trivial Delegates Example

OK, so by now, I bet you have learned a lot about delegates; however, you have every right to ask the following questions: What is the benefit of using delegates in the previous sample? Couldn’t I implement the previous example with simple old school method calls?

Well, actually if you do ask the above questions, then you are 100% correct. There is absolutely no reason why you would use delegates in such a scenario. I only included the sample to clear up the confusion around delegates, but as I indicated previously, it has no real business value. Next, let’s look at a more realistic sample where the use of delegates will start to make much more sense.

Consider a case where a developer is writing a component that performs some calculation logic for its consumers. The catch, however, is that the developer wants to make this component reusable because he wants to sell it to more than one customer, and each customer might have his own calculation logic. How can the developer write such a .NET component? The answer is obviously by using .NET delegates. Examine the code below:

public class Algorithm
{
    public delegate double Function(double x, double y);
    public static double Calculate(Function f, double x, double y, double z)
    {
        return f(x, y) + z;
    }
}

The above code illustrates the component written by the developer. He first creates a delegate called “Function” which can invoke methods that return a “double” type and accept two “double” parameters. Next, the developer defines a method called “Calculate” that accepts a parameter of type “Function” and three “double” parameters. Now, notice the key line of the code:

return f(x, y) + z;

The above line allows the component to accept, at runtime, any method that has its signature matching that of the delegate. This provides the generic behavior that the developer is looking for. Now, let’s see the component in action in order to appreciate more the power of delegates:

protected void Page_Load(object sender, EventArgs e)
{
    Algorithm.Function f = new Algorithm.Function(MyImplementation);

    Response.Write(Algorithm.Calculate(f, 3, 4, 5));
}

protected double MyImplementation(double x, double y)
{
    return x * y;
}

One of the developers working for a customer uses the component as follows: first, she creates an instance of the delegate “Function” and adds her own method called “MyImplementation” to the invocation list. Of course, notice how the signature of the method “MyImplementation” matches that of the delegate “Function”. Now, by using the line below:

Response.Write(Algorithm.Calculate(f,3,4,5));

the developer is actually utilizing the power of delegates since she is using the logic that suits her business (MyImplementation, in this case) to pass to the component.

Another developer for another customer can use the same component to fulfill his business needs, for example, by changing the implementation of the method “MyImplementation” to (x + y) instead of (x * y).

OK, so up until now, you have been introduced to .NET delegates as a powerful mechanism to perform dynamic method invocation, and you have seen some details about .NET delegates' internal representation and invocation lists. Next, I will start discussing how the .NET Framework actually utilizes delegates to implement one of its coolest (and most important) features: Events and Event Handlers.

Events and Event Delegates

In the beginning of the article, I explained the Observer pattern, and said that it is also called the Publish-Subscribe pattern and that .NET delegates are the mechanism to achieve this behavior. While you have been introduced to delegates, I have yet to tackle how delegates achieve this Publish-Subscribe design pattern: Enter .NET Event and Event Delegates.

Enough theory; trust me that the best way to quickly and efficiently understand all that there is about Event Delegates is to follow me up during the remaining two sections of this article. Off we go to the practical implementation…

Event Delegates Sample

Consider a sample business scenario: a typical company has two departments of concern, namely the Human Resources (HR) and the Employee Care (EC) departments. The HR department is responsible for recruiting, and whenever they do recruit a new employee, the EC department needs to be notified so that they can do some administration stuff related to the new employee…

First, let us examine the class defined for the first business entity: the HR class.

public delegate void NewEmployeeEventHandler(object sender, NewEmployeeEventArgs e);

class HR
{
    public event NewEmployeeEventHandler NewEmployee;

    protected virtual void OnNewEmployee(NewEmployeeEventArgs e)
    {
        if (NewEmployee != null)
            NewEmployee(this, e);
    }

    public void RegisterEmployee(string name, string sex, int age)
    {
        NewEmployeeEventArgs e = new NewEmployeeEventArgs(name, sex, age);
        OnNewEmployee(e);
    }
}

Let’s start analyzing the code:

First, a delegate called “NewEmployeeEventHandler” is created. This delegate can invoke methods that return a void and accept a parameter of type object and another parameter of type “NewEmployeeEventArgs”. NewEmployeeEventArgs is a custom class that we will examine shortly; for now, just know that this class holds information about the hired employee like her name.

Then, an event is declared as follows:

public event NewEmployeeEventHandler NewEmployee;

The above line declares an event called “NewEmployee”. So, what is “NewEmployeeEventHandler” doing in a declaration of an event? Well, as we will see later, once an event is raised, it invokes its delegate (NewEmployeeEventHandler, in this case), and the delegate then calls the method(s) – called Event Handlers – in the invocation list.

Next, a method “OnNewEmployee” is declared. This method accepts a parameter of type “NewEmployeeEventArgs”. It then raises the event as follows:

NewEmployee(this, e);

When the above line is called, the event invokes its delegate, which in turn does the method invocation of its corresponding invocation list. One more thing to notice is that raising the event “NewEmployee” conforms to the signature of its delegate “NewEmployeeEventHandler”.

Info: Before raising an event, we need to check whether there are any event listeners. In other words, we need to make sure that the invocation list is not null. This check is done via the following line of code:

if (NewEmployee != null)

Finally, there is the “RegisterEmployee” method which creates an instance of “NewEmployeeEventArgs” and calls the “OnNewEmployee” method.

Info: You might be wondering why I used a method “RegisterEmployee” to call another method “OnNewEmployee”, which in turn raises the event. Why didn’t I raise the event from the “RegisterEmployee” method directly? Well, this is a good question. The answer is that I can do that; however, as you will see in the section “.NET Event Handling Implementation”, in doing so, I am following the pattern that the .NET Framework follows to define event handling for ASP.NET pages and controls. Usually, this is the recommended practice.

Next, we will examine the NewEmployeeEventArgs class:

public class NewEmployeeEventArgs
{
    public string Name;
    public string Sex;
    public int Age;

    public NewEmployeeEventArgs(string name, string sex, int age)
    {
        Name = name;
        Sex = sex;
        Age = age;
    }
}

This class is simple. As stated before, it holds information about the newly hired employee.

The final piece of the puzzle before running the program is the EmployeeCare class:

class EmployeeCare
{
    public EmployeeCare(HR hr)
    {
        hr.NewEmployee += CallEmployee;
    }

    private void CallEmployee(object sender, NewEmployeeEventArgs e)
    {
        Console.WriteLine("Sender of event: " + sender.ToString());
        Console.WriteLine("Cusomer Info: " + e.Name + 
                          "," + e.Sex + "," + e.Age.ToString());
        //do call Employee stuff
    }
}

The constructor of the EmployeeCare class accepts an HR object. Then, it hooks itself to the “NewEmployee” event. The syntax of event hooking is:

hr.NewEmployee += CallEmployee;

The above line does the following: The EmployeeCare class is saying that it is concerned to be notified whenever the “NewEmployee” event of the HR class is raised. Now, whenever the “NewEmployee” event gets actually raised, the EmployeeCare class is assigning a method called “CallEmployee” to be the Event Handler. Again, notice that the signature of the “CallEmployee” method matches that of the delegate “NewEmployeeEventHandler” of the HR class.

So, if you are new to delegates, then the amount of information so far might be confusing, so let’s summarize where we have reached so far:

In business terms, an HR department recruits new people, and directly sends a notification that it just did. An Employee Care department is interested to receive this notification in order to do some administration tasks. In technical terms, this behavior can be achieved by using .NET Delegates an Events. The HR class – being the publisher – defines a delegate “NewEmployeeEventHandler” and an event related to this delegate “NewEmployeeEventHandler”. It then exposes a method “RegisterEmployee” which, when called, raises the event. How does the EmployeeCare class get notified whenever the event is raised? It actually defines an event handler “CallEmployee” that matches the signature of the delegate, and hooks this event handler to the HR event by using the “+=” syntax.

Finally, we are ready to run the program. The code below does just that:

static void Main(string[] args)
{
    HR hr = new HR(); //line1
    EmployeeCare cc = new EmployeeCare(hr); //line2
    hr.RegisterEmployee("mohamad", "male", 26); //line3
    Console.ReadLine(); //line4
}

Now, let’s clearly understand what is going on to kill the topic: line1 simply creates a new instance of the HR class. Line2 creates an instance of the EmployeeCare class. This will cause the EmployeeCare class to declare itself interested in the “NewEmployee” event. Recall that the constructor of the EmployeeCare class uses the following line:

hr.NewEmployee += CallEmployee;

This means that the EmployeeCare class wants to be notified whenever the “NewEmployee” event is raised, and that it has assigned the method “CallEmployee” to be the event handler.

Line3 actually raises the event by calling the method “RegisterEmployee” of the HR class. Once this line is executed, the “RegisterEmployee” method will populate “NewEmployeeEventArgs” with the supplied information (name, sex, age) and then internally raises the “NewEmployee” event. At this moment, the event invokes its corresponding delegate “NewEmployeeEventHandler”, which in turn invokes its invocation list. The invocation list contains the method “CallEmployee” of the EmployeeCare class because it was registered as en event handler.

That is all there is about .NET Delegates and Event Delegates. In the final section of the article, I will show you how to map what you have just learned into the .NET Framework internal use of delegates.

.NET Event Handling Implementation

I am sure that as .NET developers, you actually have found some familiar naming in the previous example. Precisely, the following names have definitely rang a bell (I have italicized the bell ringing portion of the word):

  • NewEmployeeEventHandler
  • NewEmployeeEventArgs
  • OnNewEmployee
  • sender
  • e

Let’s tackle this by a couple of examples:

Example1:

protected void btn_Click(object sender, EventArgs e)
{

}

The above is an event handler of an ASP.NET button “Click” event. When compared to the “CallEmployee” event handler of the EmployeeCare class, some interesting points can be outlined:

  • The “CallEmployee” event handler uses the “NewEmployeeEventArgs” class while the “btn_Click” uses the “EventArgs” class. The “EventArgs” is a .NET Framework class that indicates that there is no special information to be passed to the event handler. In our case, we needed to pass information such as name, sex, and age; so, I created a new class rather than use the “EventArgs” class, and followed the naming convention by calling it “NewEmployeeEventArgs”. Also note that the variable is called “e”…
  • Since the “btn_Click” event handler uses the “EventArgs” class, this means that the corresponding event (btn.Click, in this case) is using the delegate “EventHandler” which is a .NET Framework defined delegate. Inside the .NET Framework, this delegate is defined as follows:

    public delegate void EventHandler(object sender, EventArgs e);

    In our example, I named the delegate “NewEmployeeEventHandler” to match the naming convention of the properties class “NewEmployeeEventArgs”.

  • Both event handlers use a variable called “sender” to denote the object that raised the event.

Example2:

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
}

The above code shows the event handler of a GridViewRowDataBound” event. In this case, note that the information class is called “GridViewRowEventArgs” because it contains special information such as the current “Row”. The corresponding delegate is defined as follows:

public delegate void GridViewRowEventHandler(object sender, GridViewRowEventArgs e);

Now, you can compare the “GridViewRowEventArgs” and the “GridViewRowEventHandler” with “NewEmployeeEventArgs” and “NewEmployeeEventHandler”.

Example3:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
}

The above code shows the “OnLoad” overridable method of the ASP.NET Page object. This method internally invokes the PageLoad” event which causes the familiar “Page_Load” event handler to get fired. This method is overridable because it gives the developer – for some reason – the power to change the behavior, for example, by commenting the line “base.OnLoad(e)” and thus not firing the “Load” event.

This behavior is implemented in our example by implementing the method “OnNewEmployee” which – being overridable – gives the ability to perform additional logic such as not firing the “NewEmployee” event.

Conclusion

This article tackled the subject of .NET Delegates and Event Delegates. First, we saw how delegates can be used outside the context of events and event delegates as a mechanism to dynamic method calling. We also discussed a delegate’s method invocation list which allows calling more than one method at a time.

We also saw a delegate implementation with events and event delegates and how it can be used to achieve the Publish-Subscribe pattern. Finally, we demonstrated how the .NET Framework implements this pattern within its controls.

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