Introduction
The goals of this article is to present a very short (as possible) overview about delegates and events. The "hot" shots of this article are:
- understanding the delegates concept
- how to use events
- creating conversion operators
- goal of pre-processor
- study of attributes
Delegates
What is a delegate? A delegate is an object which reports a method. When we create a delegate, we actually create an object which will memorize a reference to a method. This method can appeal through this reference. Even if a method is not an object, this method will have a physical location inside of the memory. The Delegate
class is the base class for the delegate types. However, only the system and the compilers can derive explicitly from the Delegate
class or from the MulticastDelegate
class. It is also not permissible to derive a new type from a delegate type. The Delegate
class is not considered a delegate type; it is a class used to derive delegate types.
Most languages implement a delegate keyword, and compilers for those languages are able to derive from the MulticastDelegate
class; therefore, users should use the delegate keyword provided by the language.
The declaration of a delegate type establishes a contract that specifies the signature of one or more methods. A delegate is an instance of a delegate type that has references to:
- An instance method of a type and a target object assignable to that type.
- An instance method of a type, with the hidden
this
parameter exposed in the formal parameter list. The delegate is said to be an open instance delegate.
- A static method.
- A static method and a target object assignable to the first parameter of the method. The delegate is said to be closed over its first argument.
Now, let's see a simple example of delegates:
using System;
delegate string strMod(string str);
class DelegateTest
{
static string replaceSpaces(string a)
{
Console.WriteLine("Replace spaces with lines.");
return a.Replace(' ', '-');
}
static string removeSpaces(string a)
{
string temp = "";
int i;
Console.WriteLine("Delete spaces.");
for (i = 0; i < a.Length; i++)
if (a[i] != ' ')
temp += a[i];
return temp;
}
static string reverse(string a)
{
string temp = "";
int i, j;
Console.WriteLine("Reverse string.");
for(j = 0; i = a.Length - 1; i >=0; i--; j++)
temp += a[i];
return temp;
}
public static void Main()
{
strMod strOp = new strMod(replaceSpaces);
string str;
str = strOp("This is a test.");
Console.WriteLine("The result string is : " + str);
Console.WriteLine();
strOp = new strMod(removeSpaces);
str = strOp("This is a test.");
Console.WriteLine("The result string is : " + str);
Console.WriteLine();
strOp = new strMod(reverse);
str = strOp("This is a test.");
Console.WriteLine("The result string is : " + str);
}
}
The result of this code will be:
Replace spaces with lines.
The result string is : This-is-a-test
Delete spaces.
The result string is : Thisisatest
Reverse string.
The result string is : tsest a si siht
Now, let's study the code together a little to better understand how things are going:
- We declare a delegate named
strMod
which receives a parameter string
and also returns a string
.
- In the class
DelegateTest
, we have declared three static methods; the signature of these static methods are the same as the signature of the delegates.
- These methods make specific modifications on the strings that are received like parameters.
- The method
replaceSpaces()
uses one of the methods of the class string
named Replace()
to replace the spaces with lines.
- In
Main()
, we create a reference by type strMod
named strOp
. To this, we confer a reference to the replaceSpaces()
method. Look at the following line:
strMod strOp = new strMod(replaceSpaces);
Note how the method replaceSpaces()
is sent like a parameter.
It uses the name of the method, the list of parameters is not specified.
We will see that the replaceSpace()
method is called through the instance of the strOp
delegate, like in the next line:
str = strOp("This is a test.");
Because strOp
refers to the replaceSpaces()
method, this will be the invoked method. Then, to strOp
will be added a reference to the removeSpaces
method; after that, the strOp
delegate is called one more time. This time, the invoked method will be removeSpaces()
. Finally, to strOp
will be added a reference to reverse()
after the delegate is called. This will establish a call to the reverse()
method.
The next example is like the above one. The single difference is that the operations over the strings are encapsulated into a class named StringOps
:
using System;
delegate string strMod(string str);
class StringOps
{
public string replaceSpaces(string a)
{
Console.WriteLine("Replace spaces with lines.");
return a.Length(' ', '-');
}
public string removeSpaces(string a)
{
string temp = "";
int i, j;
Console.WriteLine("Deleting spaces.");
for(i = 0; i < a.Length; i++)
if(a[i] != ' ')
temp += a[i];
}
public string reverse(string a)
{
string temp = "";
int i, j;
Console.WriteLine("Reverse the string.");
for(j = 0, i = a.Length - 1; i >= 0; i--, j++)
temp += a[i];
return temp;
}
class DelegateTest
{
public static void Main()
{
StringOps so = new StringOps();
strMod strOp = new strMod(so.replaceSpaces);
string str;
str = strOp("This is a test.");
Console.WriteLine("The result string is : " + str);
Console.WriteLine();
strOp = new strMod(removeSpaces);
str = strOp("This is a test.");
Console.WriteLine("The result string is : " + str);
Console.WriteLine();
strOp = new strMod(reverse);
str = strOp("This is a test.");
Console.WriteLine("The result string is : " + str);
}
}
}
This program produced the same result like the first one, but in this case, the delegates refer to methods using an instance of the class StrOps
.
Multicasting
One of the most interesting facilities that delegates offer is the ability of multicasting. It is desirable to call two (or more) implementing methods through a single delegate. This becomes particularly important when handling events (discussed later in this chapter). The goal is to have a single delegate that invokes more than one method. For example, when a button is pressed, you might want to take more than one action. Two delegates can be combined with the addition operator (+
). The result is a new multicast delegate that invokes both of the original implementing methods.
In simple terms, the multicasting term designates the ability to create a method chain which will be automatically called when the delegate will be invoked. A chain like this is very simple to create. It is enough to create an instance of a delegate, and then use the +=
operator, we can add methods to the chain. To delete or eliminate a method from the chain, you have to use -=
.
Nota bene!!! We can use the +, -, and =, independent of adding and decreasing delegates, but += and -= are used more. The single restriction is that the multicast delegates have to return a result of type void
.
Down, we have an example of multicasting. This restriction rewrites the two examples above. The single modification is the return type of the methods with strings as void
, and using a ref
parameter to return the modified string in the module that has been called.
using System;
delegate void strMod(ref string str);
class StringOps
{
static string replaceSpaces(ref string a)
{
Console.WriteLine("Replace spaces with line.");
Console.WriteLine(' ', '-');
return a.Replace(' ', '-');
}
static string removeSpaces(ref string a)
{
string temp = "";
int i;
Console.WriteLine("Delete spaces.");
for (i = 0; i < a.Length; i++)
if (a[i] != ' ')
temp += a[i];
return temp;
a = temp;
}
static string reverse(ref string a)
{
string temp = "";
int i, j;
Console.WriteLine("Reverse string.");
for(j = 0; i = a.Length - 1; i >=0; i--; j++)
temp += a[i];
return temp;
a = temp;
}
public static void Main()
{
strMod strOp;
strMod replaceSp = new strMod(replaceSpaces);
strMod removeSp = new strMod(removeSpaces);
strMod reverseStr = new strMod(reverse);
string str = "This is a test";
strOp += replaceSp;
strOp += reverseStr;
strOp(ref str);
Console.WriteLine("The result string is : " + str);
Console.WriteLine();
strOp -= replaceSp;
strOp += removeSp;
str = "This is a test";
strOp(ref str);
Console.WriteLine("The result string is : " + str)
Console.WriteLine();
}
}
This program will show on screen the following messages:
Replace spaces with line.
Reverse the string.
The result string is : tsest a si sihT
Reverse the string.
Delete spaces.
The result string is : tsestasisihT.
Let's examine a little the code without going too deep in it. We can see that in the Main()
method are created four instances having a delegate type. The next three delegates refer each a specific method which modifies the characters string. After that, a multicast delegate is created which calls the removeSpaces()
and reverse()
methods. This is made in the next line:
strOp = replaceSp;
strOp += reverseStr;
First, to strOp
we apply a reference to replaceSp
. After the +=
operator, we also add the reverseStr
delegate. When strOp
is invoked, both methods will be called, replacing spaces with lines and reversing the string. The delegate is eliminated from the chain, using the next line:
strOp -= replaceSp;
after we add removeSp
using the line:
strOp += removeSp;
We invoked the strOp
delegate once more. This time, the string will be reversed and the spaces will be deleted.
Events
Event is another facility of the C# language that is very important. An event shows when an action takes place. The events are the members entity of a class. The general form of the declaration is:
event event-delegate object-name;
Let's explain a bit the general form: event-delegate represents the name of the delegate that is used for the event treatment, and object-name is the name of the event instance that is created. Let's start by looking at a simple example:
using System;
delegate void MyEventHandler();
class MyEvent
{
public event MyEventHandler activate;
public void fire()
{
if (activate != NULL)
activate(); }
}
class EventDemo
{
static void handler()
{
Console.WriteLine("The event has been made.");
}
public static void Main()
{
MyEvent evt = new MyEvent(); evt.activate += new MyEventHandler(handler);
evt.fire(); }
}
This program will show:
The even has been made.
The next example will show a multicast event.
The events can be multicast. This allows many objects to respond to an event notice. We have an example of a multicast event below.
using System;
delegate void MyEventHandler();
class MyEvent
{
public event MyEventHandler activate;
public void fire()
{
if (activate != NULL)
activate(); }
}
class X
{
public void Xhandler()
{
Console.WriteLine("Event received by an X object");
}
}
class Y
{
public void Yhandler()
{
Console.WriteLine("Event received by an Y object");
}
}
class EventDemo
{
static void handler()
{
Console.WriteLine("Event received by EventDemo");
}
public static void Main()
{
MyEvent evt = new MyEvent(); X xOb = new X();
Y yOb = new Y();
evt.activate += new MyEventHandler(handler);
evt.activate += new MyEventHandler(xOb.Xhandler);
evt.activate += new MyEventHandler(yOb.Yhandler);
evt.fire(); Console.WriteLine();
evt.activate -= new MyEventHandler(xOb.Xhandler);
evt.fire();
}
}
The program will show:
Event received by EventDemo
Event received by an X object
Event received by an Y object
Event received EventDemo
Event received an Y object.
The next program will show you how to distribute an event to three instances having type X
. The code is:
using System;
delegate void MyEventHandler();
class MyEvent
{
public event MyEventHandler activate;
public void fire()
{
if (activate != NULL)
activate(); }
}
class X
{
int id;
public X(int x)
{
id = x;
}
public void Xhandler()
{
Console.WriteLine("An event received by an instance " + str);
}
}
class EventDemo
{
public static void Main()
{
MyEvent evt = new MyEvent(); X o1 = new X(1);
X o2 = new X(2);
X o3 = new X(3);
evt.activate += new MyEventHandler(o1.Xhandler);
evt.activate += new MyEventHandler(o2.Xhandler);
evt.activate += new MyEventHandler(o3.Xhandler);
evt.fire(); }
}
This program will show:
An event received by instance 1
An event received by instance 2
An event received by instance 3
I will add to this article information about namespaces and how to use them together with delegates and events.
I hope this article has given a clear view and will be like a small guide for delegates and events.
Happy coding!!!!!