Introduction
Hi folks this time I would like to scribble down
something on C# features. Most of us (or rather, most intermediate developers) are not exploiting
all the features of C#, there are lots of features in C# that we've never heard of or even imagined.
Sometimes our ignorance of C#'s capabilities makes us write
100 lines of code instead of a single LOC that might satisfy our requirements
more easily, effectively and efficiently. So in this article I would like to explain one of the
powerful features of C# delegates.
Delegates
If somebody asks you, what is a delegate? In single
line we can say, "A delegate is a function pointer." Or we can say that a delegate is a type or an object which holds
a function's reference.
To make a delegate point towards a function we must satisfy the below
rule: The
function’s signature (which is to be pointed to by a delegate) must be the same as the pointing
a delegate’s signature.
Let’s see how a delegate points to a function practically.
In this example I am going to create an ASP.NET application to show how to use a delegate.
We are all fed up seeing the black screens of console applications in
almost all the tutorials and articles we read, so I opted for ASP.NET for
our purposes.
I am going to create an application which calculates the area of a circle.
Looks good right?
Then we are going to write a function to calculate the
area in our application.
In the above screen shot you can see the method to
calculate the area. You might be thinking, "what’s new in this?" Yes, nothing
is new in that :). If we execute the application we will be
getting an output area corresponding to the input. Here comes the hero.
There are three steps in defining and using delegates:
- Declaration
- Instantiation
- Invocation
In the above snap shot you can see that I have declared a delegate called DelCalcArea
, then inside the button
click I have created a delegate object (instantiation) and assigned a method Calculatearea
to it. I hope everything up until now is clear. To calculate area we are not
calling our CalculateArea
method directly from the button click, but we are
invoking the delegate to which our method is attached. You can see in the next
line we are invoking the DelCalcArea
delegate object with the
help of the Invoke
method (invocation), and you can see one more thing by looking
at the tool tip of the Invoke
which
accepts int
variables like our Calculate
method and also returns a double
value. Then, like any other normal method call, just assigns the delegate call
to the label in which we want to display the result.
How's this ... very simple right? One thing you
have to note is that we are not calling delegates, but invoking the delegate.
MultiCastingDeligate
You may heard of the term MultiCasting Delegate
if you tried read about delegates even once, right? Then what is multi
casting? In one line we can answer this question by saying, "Invoking multiple methods in a single line," or
"MultyCastDelegate is a linked list of Delegates."
How can we invoke multiple methods or more than one
method in one single line of code? To do multicasting we have to assign multiple methods
with the same signature to a single delegate. Let’s take an example; I got a requirement like,
want to execute 2 methods in a single LOC i.e. one method to calculate area of
circle and other to calculate area of square. And also one input value to drive
both the methods. So our code will look like:
Looking at the above code you can see that the
signature of the delegate and the signature of the methods are matching and that the
symbol += is used to
attach the function to delegate. Let’s
look at the output
Yes, it got executed successfully :) Much as we added functions with the help of +=,
we can also remove the added method by the help of -=
Using the same example, let's see
how we can remove the added Method from the delegate. From the above example we have two methods added to the delegate, and now I am going to
remove the method CalculateSquareArea
from the delegate delCalc
so
the above code will look like this:
See how I removed the method in the above snap shot? So what would our expected output be? The method CalculateSquareArea
will not be executed and the result will not be displayed in the output, right?
As expected, the function was not executed and the
result was not displayed .
User
Control Event Handler
In the previous section I tried to give you an overall
idea of what a delegate is. In this section I am going to say how a delegate is related to an event.
If there is an event then there must be an event
handler to handle the event. Delegates are typically necessary when we are using user controls in ASP.NET
applications to pull the event arguments to the parent page. Let's take a scenario and work on it.
I would like to create an area calculating application
for a circle, and I have also decided to make the area Calculator as a user
control. The area calculator consists of one text box to enter the radius of a
circle and a button to calculate the area, and the calculated area should be displayed in a label in the parent page, i.e. there is no provision in
the User-control to display the result. So the question is, can we get
the calculated area in our parent page from the user control?
The answer is yes. We can achieve this with the help of Delegates
and Events
.
Shall we start creating application?
First add an ASP.NET Project in your solution; I have
added a Project called CsharpFeatures
.
In that project I have added one user control called ChildUserControll.ascx
Don’t look at the other Project which you can see in the
solution called Calculator; we are not going to use that right now.
Let’s design the UI for the user control as I mentioned before. The UI will look like this:
Let’s go and Plumb the inner side of User Control to
take the result out. To make the calculation happen we want an event, right?
So here we can do the calculation in our calculate button click event’s event handler. OK
then. Do we get the button click event in the parent page in which the user control
is consuming? Nooo ... we will not get the event or the output from the user control.
To expose the event of the user control we have to add
an additional event in our UserControll. If we are adding an event we want an
event handler, right? And if we implement an event handler we want Event arguments, right? So what are all things we have to implement?
We have to implement: Event, Eventhandler and Event arguments. OK, let’s see
how we are going to implement all these things and how we are going to make use
of delegates.
As I mentioned before, I have already created an .ascx
control. Inside the ascx control am going to add an Event:
In the above snap shot I have added the Event called MyEvent
,
which is inside the class on top of page-load event; this is the event which will
be exposed to public or to the aspx pages in which this user control is going to
be consumed. Did you notice the type of MyEvent
? It’s MyEventHandler
. Do you think that is a system defined type? No, it's not. MyEventHandler
is a delegate. What we are doing is attaching the event handler called MyEventHandler
to the event called MyEvent
. So what will be the input params of MyEvent
?
It will be of the MyEventHandler
type, right? :) I hope you know how to declare a delegate. In either case I’ll show you the snap
shot so that you can understand it a bit better.
See the delegate? Does its signature look similar to
anything you are familiar with? Yes. Just see below the Page_Load
event’s parameters, one difference you can
see is the EventArgs
while in our delegate input it's MyEventArgs
. The EventArgs is system
defined Event arguments and MyEventArgs is defined by us. So what will be there
in MyEventArgs? And what will be the input type of the MyEvent
event?
The MyEventArgs will contain the data to be returned
from the user control after the execution of the functionality.
So in our example what is the data to be returned?
It’s the Area
, right?
Next I am going to show the MyEventArgs
class. This
class must be inherited from the System.EventArgs
class. The class
consists of one property: area.
The area is of type double. For understanding and simplicity's sake I have started
in the reverse order of construction. So what is the right order? First Create MyEventArgs
, then go for MyEventHandler
,
and finally MyEvent
which uses all the above
three things. I hope you get the point. Here we have created the entire infrastructure that is required,
but we didn’t really do anything. But if we didn’t start the calculation and didn’t assign any
value to anything without doing this, how we are going to take the result out? Let’s
go and do that.
As I said earlier we are taking the result out of the
control with the help of MyEventArgs
. We have to set the
calculated area in to the Area
Property of the MyEventArgs
for the user control. So go and do that.
So in the above snap shot you can see what I have
assigned the calculated Area to the MyEventArgs
class. The next thing we have to do is take this result out of the control. But how? To take the result out of the control, the only way is
through the event, so call the event
with the arguments we have created. Like:
This is how we call the event inside the button click
by supplying the EventArgs
. I
have consumed the control, put a debugger, and executed just to show you what is passing out
of the control. In this you can see what the objEventArgs
contains. Looks pretty good, right?
Now am going to tell you how we will consume the .ascx
control and get the value displayed in the Label, which we have placed in
the Default.aspx page. I hope you know how to place the .ascx
control in .aspx
page. Just drag and drop the control wherever you want. Then our default page
will look as shown below upon executing the control.
Let’s go to the Default.aspx page Page_Load
event and make the
necessary changes. Once we add the UserControll in to the Parent page what
actually happens is the object of the user control is created in that parent
page. In the below snap shot you can see this. Here, by adding the
ChildUserControll
in the page I get the ChildUserControll 1
object.
You can also see the event which we have declared
inside the user control in the intellisense of the object. So let’s go and
add the event handler for the event in the page.
In the tool tip you will see Press TAB to insert. Hit the tab key
two times so VS will create an event handler stub for us. You can also see
that the type of event handler that is going to be created is of MyEventHandler
type, which is the delegate we created for MyEvent
handling. All looks well and
good :)
After hitting tab the event handler stub got generated and I started
plumbing to get the area displayed in the label in the parent page. Refer to the above
snapshot. In the intellisense the Area
property is of type double. It’s
really amazing, right?
The entire code is shown below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;
namespace CsharpFeatures
{
public partial class _Default : System.Web.UI.Page
{
protected
void Page_Load(object
sender, EventArgs e)
{
ChildUserControll1.MyEvent+=new MyEventHandler(ChildUserControll1_MyEvent);
}
void
ChildUserControll1_MyEvent(object sender, MyEventArgs e)
{
lblResult.Text = e.Area.ToString();
}
}}
Look at the output and check whether the result is
getting displayed in the label in the parent page.
Wow! It’s an amazing mechanism, right? Delegates are simply amazing.
OK, here I am going to write one single simple scenario where we can use delegates.
Did you ever think of loosely coupling our applications using delegates? Yes, we
can also develop loosely coupled applications using delegates to an certain extent.
I’ll show how we can do this loose coupling. :)
Did you notice one project in our solution explorer called Calculator
? I am going to make use of
this project and we are going to create a simple calculator which performs three
arithmetical operations like Add, Subtract and Multiply with two input integer values.
For this I have created one more project (called
calculator) as a layer in our solution itself, and I have written the
calculation logic inside the Calculator Layer
.
Let’s see how I have written the Calculator Layer. Inside the layer I have created a class called MyCalculator
. It is here where I am
going to write the calculate logic. So what is the role of decoupling or loosely coupling in a simple calculator? There is a large scope for these
terms here. Let's set everything aside and think about how can we create an
application like a calculator with two layers I.E. presentation layer and business
layer.
We will write three public methods
in the Calculator layer then will call the methods from the consuming client
according to our operation. This requires the help of an if
condition or a
switch
statement. So if we want to add more functionality to our application, what will we do?
We have to go and write a method (say, Divide
) in the
Calculator layer and again make a change in the consuming application to
accommodate the change made in the Calculator
layer.
The UI Layer and the Calculator
Layer(BLL) is tightly coupled. In addition, the consuming end will eventually know
all the methods or operations in the Calculator layer because we are
using Public
methods. So how can we avoid the situation?
There are several different ways to deal with this scenario. I am going to
explain how we will do this with the help of delegates.
Let’s look at the UI of the application we are using. Look like below snap shot:
Two text boxes which can be used to enter numeric values and perform
arithmetic operations, which displayed in the dropdown. Looks cool.
Let’s go and look in to the
Calculator Layer
and see
what all plumbing I have done. As previously stated, we are going to solve this issue
with the help of delegates so we want a delegate for sure and also ensure the access
specifier must be private. Then only the
Calculator Layer
can hide
the methods within it, right? So let’s
go and see what is there in the
Calculator Layer
.
So you can see one delegate called DelegateMyCalculator
which accepts two integer type input parameters and returns integer type data. You
can also see three private methods
defined in the class. As the next step let’s go and attach the delegate to the methods. Here
is the tricky part, as before we are not directly assigning methods to delegates
but creating a method with the return type of delegate (DelegateMyCalculator
),
which also accepts one string
type of input parameter.
So looking at the above snap shot you can see the
marked line of code. In the first one the delegate method is called Calculate
,
which accepts an input parameter of string type, then inside that method we
have created an object of the delegate (DelegateMyCalculator
) and assigned it to
null. You can also clearly see that the
assigning to the delegate is done only once, and only to one method according to
the input of the Calculate
method. Let’s go and consume the method from
our UI Layer.
Here is one question which will rock your world: How can
we call a delegate method? How can we supply the input values to the delegate
method from the UI? The method accepts only one input parameter, right?
Go and see the plumbing of UI layer.
What I have done is created an object of the MyCalculator
class. Let’s see all the methods the object is
displaying in the intellisense:
You can see the method called Calculate
which is of
type
DelegateMyCalculator
and is also visible in the tool tip. We don’t have any other methods displayed
so the UI will not know all operations are in the
Calculator layer (BLL). That’s one intention. We will be passing the
operation as a selected value from the drop-down list to the
Calculate
method.
Next we
pass the input parameters to the method. Let's just see what is in the
intellisense of Calculate
method call.
Here you can see the magic. There is one method called
Invoke
which accepts two input parameters and both are integer. What will happen if
we use this method by passing the values of the textboxes as parameters to
Invoke
as integer? What will the LOC will look like?
And one more thing I have done is assigned the return
value to a label in our UI Layer. Go and execute the application and see what happens.
See? It worked fine
10 +
10= 20
; the result is displayed in the label as assigned. We can say that the
application is loosely coupled because if we want to add one more functionality,
we can go and add one method in the Calculator layer (say,
divide
) then do a very
minute change in the UI I.E. to add one operation (Divide) in the drop-down list. You
can add methods in the immediate layer with far less changes in
preceding or succeeding layer.
Conclusion
Delegates are really powerful.
Thank you