Delegates and events are one of the
coolest features provided in C#. Both delegates and events are associated with
each other, that’s why they are always talked together. For example, if we are
using events we should have delegates to support them. In fact delegates &
events in conjunction provides a way to implement one of the nicest pattern-the
observer pattern (the
publisher-subscriber pattern or pub-sub pattern).
Delegate as the word “literally” means-
“a thing which has been assigned to represent something”. In C# we declare a
delegate to represent a method.
Delegates in C# are always compared with function pointers in C++, C, which in
fact is true. Technically delegate is something which holds the address of a
method, or address of multiple methods.
Let’s look at an example and understand as to how we declare a
delegate:-
private delegate void MyDelegate();
private delegate string MyDelegate(string str);
class Program
{
private delegate
string MyDelegate(string str);
static void
Main (string[]
args)
{
MyDelegate myDelg = new MyDelegate(MyDelegateMethod1);
Console.WriteLine(myDelg("Test 1"));
myDelg += MyDelegateMethod2;
Console.WriteLine(myDelg("Test 2"));
// Would give a compile time error as the
signature of MyDelegateMethod3 does not match with delegate definition
//myDelg += MyDelegateMethod3;
Console.ReadLine();
}
private static
string MyDelegateMethod1(string strParam)
{
return "This
is from first method-"+strParam;
}
private static
string MyDelegateMethod2(string strParam)
{
return "This
is from second method-"+strParam;
}
private static
string MyDelegateMethod3(string strParam,string
strParam2)
{
return "This
is from third method-" + strParam;
}
}
From
the above program it is clear how we can use call multiple methods from a
single delegate, this is called a multicast
delegate. Notice that when we try to assign the address of MyDelegateMethod3, it gives a
compilation error highlighting the fact that delegates are type safe. Also notice how we are invoking the
methods and passing the parameters to it (by the use of brackets myDelg("Test
1")).
Behind the scene Delegates are not any
kind of magic, when you declare a delegate, the compiler actually generates an
anonymous class which is derived from System.MulticastDelegate which in turn is
derived from the base System.Delegate. In fact it would be correct to say that
when you declare a delegate you are actually declaring a class (though the
compiler internally declares the class for you). That is one of the reason why
you can declare a delegate anywhere you can declare a class i.e. inside a
namespace, inside another class. Try declaring a delegate in a method, you
would not be able to do that, think of the reason for that.
Generic Delegates Instead of declaring
a delegate every time with different parameters and return types, C# provides Action<T> and Func<T>
delegates. The Action<T> delegate can be used to reference a method with
void return type. Similarly the Func<T> delegate can be used to reference
a method with a return type. Both of these Action and Func delegates can accept
up to 16 parameters. Let’s look at an example which would make both of them
clear.
class Program
{
static void
Main (string[]
args)
{
Action delegAction =
delegActionNoParams;
delegAction();
Action<string>
delegAction1 = delegActionWithOneParam;
delegAction1("Parameter1");
Action<string,
string, string>
delegAction2 = delegActionWithMultipleParams;
delegAction2("Parameter1", "Parameter2", "Parameter3");
Func<string>
delegFunc = delegFuncNoParams;
Console.WriteLine(delegFunc());
Func<string,
string, int>
delegFunc1 = delegFuncMultipleParams;
// Would have return value 9 from the method.
int returnValue = delegFunc1(string.Empty, string.Empty);
Console.ReadLine();
}
private static
void delegActionNoParams()
{
Console.WriteLine("Call to delegate with no params and no return
types");
}
private static
void delegActionWithOneParam(string param1)
{
Console.WriteLine("Call to delegate with param={0} and no return
types",param1);
}
private static
void delegActionWithMultipleParams(string param1,string
param2,string param3)
{
Console.WriteLine("Call to delegate with params={0},{1},{2} and no
return types",param1,param2,param3);
}
private static
string delegFuncNoParams()
{
return "Func
delegate with no params";
}
private static
int delegFuncMultipleParams(string param1,string
param2)
{
return 9;
}
}
The
program above is self explanatory. It gives us a clear idea as to how we can
make appropriate use of Action and Func delegates and avoid unnecessary
“delegates” declaration. Imagine how many delegates you would have to declare
for the above program if you were not using Action and Func. Also Action and Func comes in very handy when we have to dynamically invoke methods,
you can realize its strength by imagining such a scenario.
Lambda expression can be of great help
with delegates. Below is the example which simplifies the code involving delegates:-
{
Func<string,
string, string>
lambdaDeleg = (str1, str2) => string.Format("This is an example of lambda delegate with two
paramters={0} and {1}", str1, str2);
Console.WriteLine(lambdaDeleg("Parameter1","Parameter2"));
Console.ReadLine();
}
The above code demonstrates the use of lambda
expression with delegates. Notice how we can specify the parameters and the
method body with the lambda(=>).
Events
Having
learned delegates lets try making real use of them. Delegates are used a lot to
support events. Let’s dig deep into what actually are events. Literally it
means “happening of something”. Since our OOPS is based on real world so events
are also accommodated in our programming scenario- it means occurrence into an
application. As I mentioned before that Delegates & Events together
provides a very famous pattern-the observer
pattern or the publisher-subscriber pattern. (We will talk about this
pattern in our next article). Have you ever used events or delegates before? -
A definite yes. In fact you have been using those, quiet a lot. For example you
are displaying a message box on click of a button. Here the “button click” is
the event and the method body where
you write the code to show the message box, is the supporting delegate for the event. Let’s simply jump to a real world example where we would
make use of events.
You often might have come across
various web pages where they have a “Notify Me” link. This notification can be
related to new product availability or something else. Imagine we have to implement
such a scenario where a departmental
store would want to notify the shoppers
through emails that a new stock has arrived. Events-delegates are very
efficient tools for such a scenario. Below is the complete code which
implements the idea discussed above. Let’s take the snippets one by one and
understand them.
public class NewProductEventArgs:EventArgs
{
public string
NewProduct { get; set;
}
public NewProductEventArgs(string productName)
{
this.NewProduct = productName;
}
}
This
class serves as placeholder for the argument that events can have. An event can
have no arguments as well but usually we define this class to contain objects
related to our business requirements. (We will find out its use in next class)
public class DepartmentStore
{
public event
EventHandler<NewProductEventArgs>
NewProductArrived;
public void
NewProduct(string productName)
{
if (NewProductArrived != null)
{
NewProductArrived(this, new NewProductEventArgs(productName));
}
}
}
This
class declares an event NewProductArrived.
Notice how this event is declared. The EventHandler is a generic delegate which represents the
method that will handle the event. The generic parameter for this is the class
which we have created above (NewProductEventArgs).
The delegate “EventHandler” requires its generic parameter to be derived from
EventArgs that is why we have declare the class as public
class NewProductEventArgs:EventArgs. When we use the delegate
“EventHandler” it requires that the function definition for this should have
first parameter as object and second
parameter of type derived from EventArgs.
We
have then created the method NewProduct. This method would act as our publisher which would notify the subscribers (subscriber class coming
below) that an event has occurred. Notice how we check NewProductArrived event
for null. If no subscriber has subscribed for NewProductArrived event this would
be null. The call inside the if check is
very important. Using NewProductArrived
with brackets would invoke all the handlers which are methods subscribed to
this event. (Remember we had learned in Delegates that delegate name with
brackets invokes the methods?).
{
this.ShopperName = shopperName;
}
public void
RecieveNewProductNotification(object sender, NewProductEventArgs e)
{
Console.WriteLine("Shopper {0} thanks for subscribing to our
notifications. New product {1} has arrived.", this.ShopperName, e.NewProduct);
}
This
class is the subscriber for the
publisher mentioned above. The subscription is done in the form of the method RecieveNewProductNotification. Since
the signature of this method fulfills the requirement of delegate EventHandler(
object and NewProductEventArgs as paramters) so it acts as a delegate method
for EventHandler.
static void Main (string[] args)
{
DepartmentStore departmentStore = new DepartmentStore();
Shopper shopper1 = new Shopper("John");
departmentStore.NewProductArrived +=
shopper1.RecieveNewProductNotification;
departmentStore.NewProduct("Dishwasher");
Shopper shopper2 = new Shopper("Mary");
departmentStore.NewProductArrived +=
shopper2.RecieveNewProductNotification;
departmentStore.NewProduct("Washing
Machine");
departmentStore.NewProductArrived -=
shopper1.RecieveNewProductNotification;
departmentStore.NewProduct("Furniture");
Console.ReadLine();
}
The
main method shows how we publish the events. What I am doing with += and -= is
subscribing and unsubscribing to the events.
The NewProduct() method in the DepartmentStore class fires or publishes
the event NewProductArrived. All the subscribers are subscribed to the event
with the lines departmentStore.NewProductArrived
+= shopper2.RecieveNewProductNotification;
Below
is the output of the code (All of the above code should be simple to execute in
a console application and is self explanatory).
The
output above is interesting. Since the shopper John was already subscribed to
the event so he was informed for the arrival of washing machine as well. But
when he was unsubscribed with -= he was not informed about the Furniture. (Look
carefully in the main method for the cause of the output).
Events and delegates are powerful
concepts provided in C# and are very handy when different “objects” of a system
are interested in various “happenings” in the system.
Feel
free to add more to this or rectify this. Happy coding!
Very nice content specially Func and Action
ReplyDeleteHi Akhil apologies for the delayed reply and thanks for the nice comments! I usually don't write here anymore I am writing my articles on codeproject now so may be you can have a look there.
DeleteThanks a lot for your valuable comments!
This comment has been removed by the author.
ReplyDeleteThis was really great for a beginner like me to get my head around these widely used concepts.
ReplyDeleteGreat work!
This was excellent article about delegate and event. Well explained.
ReplyDeleteThanks Sanjeeva. I am no more active here, i am writing at codeproject now.
ReplyDeleteExcellent Article! After years of being in development I finally got my head around delegates! Thanks a bunch
ReplyDelete