Introduction
Delegates and Events are two very powerful features of .NET. Events are the cornerstone of GUI programming and Delegates are used in implementing them. In this article, I discuss briefly how delegates work and how they are made use of to implement events.
Table of Contents
What is a delegate?
Delegates in the .NET framework are equivalent to function pointers. However, the C# Language Specification also adds: "unlike function pointers, delegates are object oriented and type-safe�.
Delegates allow you to pass references of entry points to methods and invoke them without making an explicit method call. It also defines the signature of the method it can call.
For example:
public delegate int MyDelegate(string somevalue);
This delegate may be used to invoke methods that return an integer and take a string as a parameter. In order to make use of this delegate, you need to instantiate it, to specify the method that needs to be called.
For example:
public void MyMethod()
{
MyDelegate a = new MyDelegate(MyDelegateMethod);
}
Here MyDelegateMethod
is a method that has a signature similar to that of MyDelegate
.
Note: If signatures of method and delegate do not match, the C# compiler reports an error such as:
td.cs(38,46): error CS0123: Method 'DelegateDemo.SampleClient.MyEvent()'
does not match delegate 'void DelegateDemo.CustomTextEvent()'
A delegate is used to invoke a method similar to how a method call is made.
For example:
a(�This is a test invocation�);
Thus, there are three steps in defining and using delegates:
- Declaration
- Instantiation and
- Invocation
A complete example follows:
using System;
namespace SimpleDelegateExample{
public delegate void SimpleDelegate();
class TestDelegate
{
public static void ActualFunction()
{
Console.WriteLine("called by delegate..");
}
public static void Main(){
SimpleDelegate sd = new SimpleDelegate(ActualFunction);
sd();
}
}
}
A couple of interesting points to note �
A delegate does not know or care about the class of the object that it references. Any object will do; all that matters is that the method's argument types and return type match the delegate's. This makes delegates perfectly suited for "anonymous" invocation.
If an invoked method throws an exception, the method stops executing, the exception is passed back to the caller of the delegate, and remaining methods in the invocation list are not invoked. Catching the exception in the caller does not alter this behavior.
How delegates work?
The .NET framework documentation details how delegates work.
�A delegate declaration defines a class that is derived from the class System.Delegate
. A delegate instance encapsulates an invocation list, which is a list of one or more methods, each of which is referred to as a callable entity. For instance methods, a callable entity consists of an instance and a method on that instance. For static methods, a callable entity consists of just a method. Invoking a delegate instance with an appropriate set of arguments causes each of the delegate instance's callable entities to be invoked with the given set of arguments.�
Events
The C# programmer�s reference defines an event as follows:
�An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object. The most familiar use for events is in graphical user interfaces; typically, the classes that represent controls in the interface have events that are notified when the user does something to the control (for example, click a button)."
Declaring an event is directly tied to a delegate. A delegate object encapsulates a method so that it can be called anonymously. An event is a mechanism by which a client class can pass in delegates to methods that need to be invoked whenever �something happens�. When it does, the delegate(s) given to it by its clients are invoked.
To declare an event in C# use the following syntax:
public delegate void testDelegate(int a);
public event testDelegate MyEvent;
Once an event is declared, it must be associated with one or more event handlers before it can be raised. An event handler is nothing but a method that is called using a delegate. Use the +=
operator to associate an event with an instance of a delegate that already exists.
For example:
Myform.MyEvent += new testEvent(MyMethod);
An event handler may also be detached as follows:
MyForm.MyEvent -= new testEvent(MyMethod);
In C#, events may be raised by just calling them by a name similar to method invocation, say MyEvent(10)
. The example given in the next section will help you understand events better.
How does the event keyword work ?
Whenever an event is defined for a class, the compiler generates three methods that are used to manage the underlying delegate� i.e.:
add_<EventName>
:
This is a public method that calls the static Combine
method of System.Delegate
in order to add another method to its internal invocation list. This method is however not used explicitly. The same effect is achieved by using the +=
operator as specified before.
remove_<EventName>
:
This is also a public method that calls the static Remove
method of System.Delegate
in order to remove a receiver from the event�s invocation list. This method is also not called directly. Its job is done by the -=
operator.
raise_<EventName>
:
A protected method that calls the compiler generated Invoke
method of the delegate, in order to call each method in the invocation list.
Delegates and Events in Windows Forms
Default delegates are provided for the events of the controls and classes of the .NET Framework base class library. When using Windows Forms to create rich client applications, events can be handled in two ways�
- By declaring a method that has a signature that is the same as the corresponding event handler delegate and then registering the method as a handler (using
+=
).
- By overriding the corresponding event method, when your class is a subclass of the
Control
class.
For example, the Paint
event (equivalent to a WM_PAINT
) in a Windows forms application can be handled as follows:
Registering event handlers with delegates:
Declare a method with the same return types and arguments as the PaintEventHandler
delegate:
static void MyPaintHandler(object sender, PaintEventArgs pea){
}
and then install this handler for a particular instance�
myform.Paint += new PaintEventHandler(MyPaintHandler);
Overriding member functions:
Inside a class derived from Form
(Control
), you can simply override the OnPaint
method:
protected override void OnPaint(PaintEventArgs pea){
}
At this point, it is quite natural to think that the base class method (which can be overridden to handle an event) is just an event handler, that has already been installed. It is actually the other way around. It is the responsibility of the base class method to actually invoke all the installed event handlers. Let me give an example to illustrate this point:
using System;
using System.Windows.Forms;
namespace DelegateDemo{
public delegate void CustomTextEvent();
class CustomTextForm : Form
{
private string m_sText=null;
public event CustomTextEvent CustomTextChanged;
public string CustomText
{
get
{
return m_sText;
}
set
{
m_sText = value;
OnCustomTextChanged();
}
}
protected virtual void OnCustomTextChanged()
{
if(CustomTextChanged!= null)
CustomTextChanged();
}
}
class SampleClient
{
public static void Main(){
CustomTextForm a = new CustomTextForm();
a.CustomTextChanged += new CustomTextEvent(SampleClient.MyEvent);
a.CustomText = "Welcome !";
Application.Run(a);
}
public static void MyEvent()
{
Console.WriteLine("Custom text changed");
}
}
}
Code Discussion:
For the sake of discussion, assume you need to write a custom Form
class that has a property called CustomText
. It is actually a string that can be set or obtained. Whenever the text is set, an event is fired. This is declared as:
public event CustomTextEvent CustomTextChanged;
and the corresponding delegate is also defined:
public delegate void CustomTextEvent();
There is also an event handling method of the base class (your class) that is called whenever this event occurs. This is given as:
protected virtual void OnCustomTextChanged()
{
if(CustomTextChanged!= null)
CustomTextChanged();
}
The client class also registers a new event handler �
a.CustomTextChanged += new CustomTextEvent(SampleClient.MyEvent);
When you run this sample, the MyEvent
method is called when the CustomText
property is changed.
Note:
The following code is mandatory:
if(CustomTextChanged!= null)
CustomTextChanged();
because CustomTextChanged
is likely to be null
, if no handlers have been registered at the time the CustomText
property is set.
The OnCustomChanged
method is declared virtual
so that it can be overridden by subclasses.
Further Reading
Once you are quite familiar with delegates, you would find the following articles interesting: