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

Delegates in C# - Attempt to Look Inside - Part 3

0.00/5 (No votes)
8 Oct 2010 2  
More about delegates. Meet the Event

Background

This is the third article out of four where I continue to discuss delegates and introduce events.
Because the articles are logically and contextually connected, I would recommend reading them accordingly.

In my first article, I discussed delegates and the way C# compiler treats them. We already know that a delegate is an object of a specific type. It is created by the compiler, it has references to one or many functions; when it calls the Invoke method, it actually calls the referenced function(s).
This is the link to Delegates in C# - Attempt to Look Inside - Part 1.

The second article explains how a delegate can use multithreading. The delegate can use the Invoke method to call the referenced function(s) synchronously or BeginInvoke/EndInvoke methods to make an asynchronous call.
This is the link to Delegates in C# - Attempt to Look Inside - Part 2.

More about Delegates: A Different Approach

After I published the first two articles, I received some positive feedback. Many programmers liked the approach of explaining programming terms not only using definitions and code examples but also making up real life situations and to try to translate them into programming. When you create strong associations between real life and programming, it helps you program better. Your program comes to life; you can see where and how to use programming techniques that you've never used before.

Let’s take delegates as an example.

We use delegates in everyday life without even knowing it. Every now and then we order food by phone, buy things online, take online courses; talk with friends via email, etc... Look:

  1. Order food by the phone: It is a delegate representing the restaurant preparing food for you.
  2. Buy things online: The website is a delegate representing a seller.
  3. Take online course: The website is a delegate representing a college.
  4. Talk with a friend via email: Email is a delegate representing your friend.

Never thought about it? But it's true. A delegate is not only a person representing somebody at a conference or a meeting but also anything that can represent anything.

The notion of phones, websites, etc... basically is a delegates declaration in programming world. You globally declare the phone delegate, website delegate, or email delegate. They can be accessed by everyone but they are not the real thing.

To make them real, you have to instantiate them. In the real world, you cannot just call using a phone, you call using a specific phone number.
So when you get a number for your call, you create an instance of the phone delegate.

This number belongs to something (a restaurant for example). This is a method or function in the programming world. So when you actually call the number, you invoke the delegate.

And we can pass the delegates. You can pass a phone number of a restaurant to a friend. Now your friend can use this delegate.

But there is more in this.
Using a phone, you can order not only food but also talk to people, order tickets, make an appointment, etc…

So, one delegate – a phone – can represent many different things. It depends on the phone number and the message that you pass via the phone.

We don’t need to have four different phones in our apartment to call for four different things. One phone is sufficient.
Do you get the idea?

The same delegate can be used for many different functions/methods. Just the signature should be observed.

And how does this apply to programming? Imagine that you have a procedure that logs error messages into a file. It returns void and accepts a string parameter: error message.

Another procedure logs error messages into a database. It returns void and accepts a string parameter: error message.

Another procedure emails error messages to an admin. It returns void and accepts a string parameter: error message.

You can declare a public delegate representing any of these procedures. It must have the procedures’ signature: it returns void and accepts a string parameter.

In the program, you create an instance of this delegate pointing to a procedure that is more appropriate. When you use the delegate’s instance, it invokes the proper procedure and logs an error message the way you want it.

Events

Using this approach, I want to take a close look at events. There is no doubt that a C# programmer uses events a lot. I cannot imagine any production program without events. That means that everyone knows how to use events and this creates a challenge for me to bring something knew into the topic. Nevertheless, I will try to find another angle in my explanation.

What is an event?

According to the Google dictionary, an event is a thing that happens, esp. one of importance.

If we want to single out something really important to us, we would make an event for it.

In order to bring the association closer to programming, let us assume that we don’t know when the event occurs.

Example: A family is waiting for a baby. The exact delivery date is naturally unknown.

They want family and friends to visit the future mother in the hospital. So they announce the event. They publish a message on Facebook asking potential participants to let them know if they are coming.

If I am a friend and want to come, I would call them and give my phone number asking them to notify me when it happens.

When the event occurs, they call and notify me and I will come and visit the family in the hospital.

Do you want to plunge deeper into this? Follow me then.

When the family announced the event, they actually brought a delegate in place. It is not instantiated yet.

If it was instantiated, it would be used to notify all participants.

The first caller creates an instance of this delegate.

A special function must be created and the reference to this function is placed into the delegate.

When an event occurs, they run the event’s Invoke method (if the event is not null: they have participants). Every participant will be notified by running the function created for the event.

In programming terms, the family published an event and participants subscribed for the event.

Important Disclosure

An event is a delegate. Everything that we know about delegates, you can apply to an event. By publishing an event, you select a proper delegate type from previously declared delegates or from pre-constructed by the environment delegates.

Event Modifies the Delegate

At the same time, an event modifies this delegate so we can say that the event keyword is a delegate modifier.

Let us consider some differences.

  • Even though an event is a delegate, delegate declaration cannot replace event declaration. Delegates must be declared earlier in order to become an event.
  • Event declaration does not specify the function signature, it uses the pre-defined type.
  • A participant cannot subscribe to a delegate. You must declare (publish) the event in order to enable subscription.
  • The event belongs to its class. You cannot invoke the event outside its class (as to a delegate – you can invoke it from any place).
  • If you define an interface, you cannot declare a delegate as a part of the interface but you can do it for event.
  • .NET applies some restriction on a delegate that you want to use as an event. It requires that the function signature was void() (object sender, EventArgs e).

Code

MyFamilyEventHandler is a delegate that we prepared to be a source for a future event:

// declare delegate for future event
public delegate void MyFamilyEventHandler(object sender, EventArgs e);

Family class declares an event OnBabyBorn. The method BabyBorn raises the event. It checks if the event is not null, in other words it checks that the event has subscribers.

The event has a type of MyFamilyEventHandler.
The base class it inherits from is MulticastDelegate (See Picture 1). This proves that an event is just a delegate by nature.

eventtypedebug.gif

Here goes the Family class:

/// <summary>
/// Family that waits for a baby
/// </summary>
public class Family
{
	///<summary>
	/// declare event of MyFamilyEventHandler
	/// type
	/// </summary>
	public event MyFamilyEventHandler OnBabyBorn;

	public string Name { get; set; }

	///<summary>
	/// constructor
	/// </summary>
	///<param name="name">
	public Family(string name)
	{
		Name = name;
	}

	///<summary>
	/// raise OnBabyBorn event;
	/// </summary>
	public void BabyBorn()
	{
		if (null != OnBabyBorn)
			OnBabyBorn(Name, new EventArgs());
	}
}

Now I created the Friend class.
Friend class has a public property Family.

When this property is being assigned (within Set procedure), the friend subscribes for family event OnBabyBorn. Subscription is done by using the following code:

family.OnBabyBorn += new MyFamilyEventHandler(family_OnBabyBorn); 

As you already know, when a compiler meets this code, it creates an instance of the event (which means a delegate) and assigns the reference of the function family_OnBabyBorn to the delegate. From now on, when the event is raised (which means that it's the base delegate invoke), the function is called and executed.

Remember, the delegate resides at family class. So the friend provides the family with his method information through the delegate that will be invoked when event occurs.

///<summary>
/// Family friend
/// </summary>
public class Friend
{
	private Family family;

	public Family Family
	{
		get
		{
			return family;
		}
		set
		{
			family = value;

			//subscribe for the family OnBabyBorn event:
			family.OnBabyBorn += new MyFamilyEventHandler
						(family_OnBabyBorn);
		}
	}

	public string Name { get; set; }

	///<summary>
	/// constructor
	/// </summary>
	///<param name="name">
	public Friend(string name)
	{
		Name = name;
	}

	///<summary>
	/// run when OnBabyBorn event in 
	/// family occurs
	/// </summary>
	///<param name="sender">
	///<param name="e">
	void family_OnBabyBorn(object sender, EventArgs e)
	{
		Console.WriteLine("{0}, go visit {1} family", Name, sender.ToString() );
	}
}

Finally the Program class.

It creates Family object and two Friend objects.

It assigns the family to the friends. It runs family BabyBorn procedure, which triggers the OnBabyBorn event.

Both friends are notified that the event occurred and the friend’s pre-assigned function is called.

class Program
{
	static void Main(string[] args)
	{
		//create new family:
		Family family = new Family("Adams");

		//create a friend:
		Friend Ed = new Friend("Ed");

		//assign the family to the friend:
		Ed.Family = family;

		//create another friend:
		Friend Alex = new Friend("Alex");

		//assign the family to the friend:
		Alex.Family = family;

		//baby is born
		family.BabyBorn();

		Console.Read();
	}   
}

Here is the output:

output.gif

Notes

By associating programming subjects (delegates and events) with real world subjects, I tried to show you that in your code, you should use similar logic. Delegates and events in programming have the same meaning as in the real world. I intentionally created a very simple example of how to use events. The idea is just to show common sense behind events in programming.

I hope this will help to better understand delegates and events and use them in your code.
The next article I am working on is to introduce you to the modern world of delegates.

To be continued...

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