1. Introduction
Delegate is a reference type just like other object. When you create an object, the memory is allocated for the object on the heap and reference for it is stored in the reference variable, which is in the stack. Consider the below statement:
Organization Org = new Organization("C# Corner", staff1, staff2, staff3, staff4 );
Here, the organization
object is created on the Heap memory and a reference to that memory location is stored on the stack identified by the token Org
. Like the Org
reference, the delegate reference type will refer to the function address. At runtime, the function will be loaded into a code segment of memory like Heap Segment for object created using new
keyword. If we take the starting address of the function (first line of translated code) in the code segment and store it in a reference variable, we call that reference variable a Delegate
.
2. Declaring a Delegate
Below is the syntax for declaring a delegate
:
{scope}delegate {returntype}DelegateName({parameters });
In the above syntax:
- Scope: It is access specification like
public, private
, etc.,
delegate
: keyword
- returntype: Function return type
- DelegateName: Name of the delegate
- Parameters: Function parameter names with types
Once delegate is declared, you can create the instance of the delegates. Just think about the class below:
class Publishers {}
The class
keyword is used to specify the token Publishers
as a class template. Later, you can create the object of template type Publishers
. The same holds true for delegate
s. The above syntax just shows how to declare a delegate
. Consider the below example for the syntax:
public delegate int GetTotalDelegate(Staff[] staffs);
In the above declaration, we are told that there is a delegate named GetTotalDelegate
, which takes Array of Staff as parameter and returns an integer to the caller. Later, you can create an instance of the delegate
type GetTotalDelegate
.
3. Creating a Delegate Reference
Now look at the below statement:
GetTotalDelegate Salary_Total = new GetTotalDelegate(Total_Salary );
In the above statement, we created the instance of delegate
reference. What reference? Salary_Total
. What is the type of Reference? GetTotalDelegate
. As you see, you are actually creating an object of type GetTotalDelegate
. Now, go ahead and look at the syntax example once again. Got, the clue? Right. As per the example, the compiler will actually create a class of type GetTotalDelegate
and accepts any function name (Takes it as address reference) that takes an array of Staff
and returns an integer. Here Total_Salary
is the name of the function we are passing in and that function takes an Array of Staff
and returns an integer.
Boring? OK. Let me walk you through an example.
4. The Staff Class
This class is self-explanatory. It has some field members, a constructor to initialize them and a ToString
override. Below is the class:
public class Staff
{
private int StaffId;
private string StaffName;
public int Salary;
public int Bonus;
public Staff(int id, string name, int Salary, int bonus)
{
StaffId = id;
StaffName = name;
this.Salary = Salary;
Bonus = bonus;
}
public override string ToString()
{
return string.Format("{0} - {1}", StaffName, StaffId);
}
}
5. The Organization Class
This class has Array of staff
s who forms the Organization
.
- First, a delegate is declared. The delegate name is
GetTotalDelegate
and it takes array of staff as parameter and returns an integer. Below is delegate:
public delegate int GetTotalDelegate(Staff[] staffs);
- Next, two member variables are placed in this class. One is Array of
staff
and other one is for Name of the Organization.
private Staff[] Staffs;
private string Org_Name;
- The constructor will initialize the internal members. Constructor code is given below:
public Organization(string Org_name, params Staff[] staffs)
{
Staffs = new Staff[staffs.Length];
for(int i=0; i < staffs.Length; i++)
Staffs[i] = staffs[i];
Org_Name = Org_name ;
}
- The
Calculate_Total
function takes the delegate of type GetTotalDelegate
as parameter. Makes a call to the function referred by the delegate and returns the return value of the delegate parameter delegateRef
. Note that when we are making a call with delegate, the parameter passed-in is Staff
array and as return value integer, the function Calculate_Total
returns an integer. Here, we do not bother what is implemented by the function that came as the parameter in the form of delegate. Below is the Function that receives function as parameter (Delegate
) and returns an integer:
public int Calculate_Total(GetTotalDelegate delegateRef)
{
return delegateRef(Staffs);
}
- The
DisplayStaffs
function walks through the Staffs
array and prints the staff
object. Note, the ToString
override is called as the Console.WriteLine
tries to represent the Staff
in string
format. Below is the function:
public void DisplayStaffs()
{
foreach(Staff staff in Staffs)
Console.WriteLine(staff);
}
Full Organization class is given below:
public class Organization
{
public delegate int GetTotalDelegate(Staff[] staffs);
private Staff[] Staffs;
private string Org_Name;
public Organization(string Org_name, params Staff[] staffs)
{
002_3.1: Initialize the Staffs Array
Staffs = new Staff[staffs.Length];
for(int i=0; i<staffs.length; staffs[i]="staffs[i];"> Org_Name = Org_name ;
}
public int Calculate_Total(GetTotalDelegate delegateRef)
{
return delegateRef(Staffs);
}
public void DisplayStaffs()
{
foreach(Staff staff in Staffs)
Console.WriteLine(staff);
}
}
6. The Calculate Utility Class
If a class has all static
functions in it, we call it a utility class. As all the members of the class are static
, the clients do not need to create an instance and instead they can directly access the function by using the class name. This class implements two functions. One function calculates Total salary and the other function Calculated Total Bonus. Note these function signatures map the delegate we declared in the Organization
class. That is both the functions receive Staff
Array as parameter and return an integer. The Organization
class delegate is going to use these functions and you will see that sooner. Below is the Utility class [hope no more explanation is required]:
public class Calculate
{
public static int Total_Salary(Staff[] Staffs)
{
int sum = 0;
foreach(Staff staff in Staffs)
sum = sum + staff.Salary ;
return sum;
}
public static int Total_Bonus(Staff[] Staffs)
{
int sum = 0;
foreach(Staff staff in Staffs)
sum = sum + staff.Bonus ;
return sum;
}
}
7. Delegate Usage
Let us see how the user of the above classes uses the delegate. First instances of four Staff
are created.
Staff staff1 = new Staff(100, "Mahesh Chand", 100000, 10000);
Staff staff2 = new Staff(101, "Mike Gold", 80000, 120000);
Staff staff3 = new Staff(102, "Sundar Lal", 70000, 25000);
Staff staff4 = new Staff(103, "Praveen", 50000, 27000);
Next, create the Organization
for our example by passing in all the staff
s created. The Organization
class will copy the array and holds it in the class array member variable Staffs
.
Organization Org = new Organization("C# Corner", staff1, staff2, staff3, staff4 );
Org.DisplayStaffs();
Next, two delegate instances Salary_Total
, Bonus_Total
of the same type GetTotalDelegate
are created. Note that for the constructor of this delegate, we are passing the function name that matches the delegate by the arguments it receives and type it returns. The compiler by reading the delegate
keyword defines a class called GetTotalDelegate
. Well, that is behind the scenes of how delegates work. If you are not a beginner who needs extra explanation, download the attached source code and examine the EXE file with ildasm
. Look at the internal class GetTotalDelegate
that is by the way internal for our Organization
class.
Organization.GetTotalDelegate Salary_Total =
new Organization.GetTotalDelegate(Calculate.Total_Salary );
Organization.GetTotalDelegate Bonus_Total =
new Organization.GetTotalDelegate(Calculate.Total_Bonus );
Once the delegate
s are created, the total expense spent by the Organization
is computed by calling the function Calculate_Total
of Organization
class. This function expects GetTotalDelegate
delegate as parameter. GetTotalDelegate
is the wrapper class created by the compiler that points to the function in the code segment. Calculate_Total
function just makes a call to the function pointed by the wrapper class GetTotalDelegate
and returns the Integer. We are making two calls to the Calculate_Total
function. First, by sending the Wrapper Class GetTotalDelegate
(the delegate type) that points to Total_Salary
function of utility class and the second time same type of parameter pointing to different function Total_Bonus
. Below is the code:
to the Organization class member function.
int Total_Org_Expenses;
Total_Org_Expenses = Org.Calculate_Total(Salary_Total) + Org.Calculate_Total(Bonus_Total);
Console.WriteLine("Total Expense : " + Total_Org_Expenses );
8. Explore Delegate by ILDASM
Download the demo and examine the EXE using the ILDASM. For beginners [How do I]:
- Open the console window using Microsoft Visual Studio => Visual Studio Tools => Visual Studio Command Prompt.
- Move to the directory where you extracted the zip file, which has the EXE.
- Type ildasm .exe
- Examine the to see how the statement
public delegate int GetTotalDelegate(Staff[] staffs);
converted by the compiler as internal class of type GetTotalDelegate
9. Multicast Delegate
In the previous part, we saw how we can use a simple delegate
. In the article, I will explain what is a multicast delegate and how we create and use it. Multicast delegates are the combination of two or more delegates of the same type and they together form a delegate chain. Each participant in the delegate chain should have a void
return type. Let us take an example of an Order processing system that makes use of the Multicast delegate. The example I created has an OrderShipment
class and that makes use of Multi-cast delegate. First, we will explore the OrderShipment
class and then we will move the client code that makes use of the OrderShipment
class and Multicast delegate
.
10. OrderShipment Class
This class breaks the Order processing into a small group of functions that will match the type single delegate, which is eligible for delegate chaining.
- 1) In this class, first a multicast
delegate
is declared. Actually, it is normal delegate
syntax only. Soon, we are going to use this for the purpose delegate
chaining. The delegate
accepts Order id and customer id as parameter and returns nothing that is void
. Please keep in mind that multicast delegate
principle work for void
return type only. There is no restriction on the Parameters that it receives. Below is the Delegate
declaration:
public delegate void OrderProcessingMethods(int OrderId, int CustomerId);
- The function split into five parts is actually used for the Order processing. The split functions that will become part of the delegate chaining are shown below:
public void GetShoppingCartItems(int OrderId, int CustomerId)
{
Console.WriteLine("(1) GetShoppingCartItems");
Console.WriteLine("===============================");
Console.WriteLine("All shopping Cart Items are Collected.");
Console.WriteLine("Formed a Order with supplied Orderid");
Console.WriteLine("______________________________________________________");
}
public void CalculateOrderPrice(int OrderId, int Customerid)
{
Console.WriteLine("(2) CalculateOrderPrice");
Console.WriteLine("===============================");
Console.WriteLine
("Price of each products collected from the shopping cart summed up");
Console.WriteLine("Order Price calculated");
Console.WriteLine("_______________________________________________________");
}
public void CalculateDiscount(int OrderId, int Customerid)
{
Console.WriteLine("(3) CalculateDiscount");
Console.WriteLine("===============================");
Console.WriteLine("Get the Discount amount for the VIP");
Console.WriteLine("Reduce Order Price");
Console.WriteLine("______________________________________________________");
}
public void AwordFreeGifts(int OrderId, int Customerid)
{
Console.WriteLine("(4) AwordFreeGifts");
Console.WriteLine("===============================");
Console.WriteLine("Regular Customer. Pick up a gift");
Console.WriteLine("Place the gift item in the Order for free");
Console.WriteLine("______________________________________________________");
}
public void GetOrderConfirmation(int OrderId, int Customerid)
{
Console.WriteLine("(5) GetOrderConfirmation");
Console.WriteLine("===============================");
Console.WriteLine("Order confirmation screen shown to the User");
Console.WriteLine("Order Confirmed");
Console.WriteLine(".");
}
Note, there is nothing more than the call to Console output functions. But, I hope you understand how these functions will be in real world applications.
- This class has a
Member
function that accepts the Multicast delegate
as parameter and then makes a call to the multicast delegate. The client will make the delegate chain based on the above five functions and then calls this member function:
public void ProcessOrderShipment
(OrderProcessingMethods ProcessToFollow, int Orderid, int Customerid)
{
ProcessToFollow(Orderid, Customerid);
}
Implementation of this class is completed now. Time to go for Delegate
chaining.
11. Client Code
The client will process the order shipment differently for three types of customers. The customer types are:
- Normal customer
- Regular customer who makes purchases monthly once or twice
- The VIP customer who has built up a good relation
For normal customer, there is no discount and surprising gifts. The regular customer will have surprising gifts based on the order cost. And VIP customer has a discount as well as gifts. Now lets us go through how the client code makes use of the multicast delegate.
- First, the instance of
OrderShipment
class is created. Code is below:
OrderShipment deliverorders = new OrderShipment();
- Next, the delegate of type
OrderProcessingMethods
is declared. This delegate variable is used as multi cast delegate later.
OrderShipment.OrderProcessingMethods orderprocess;
- Five
delegate
instances are created and they point to one of the five methods implemented by the OrderShipment
class.
OrderShipment.OrderProcessingMethods process1 =
new OrderShipment.OrderProcessingMethods(deliverorders.GetShoppingCartItems);
OrderShipment.OrderProcessingMethods process2 =
new OrderShipment.OrderProcessingMethods(deliverorders.CalculateOrderPrice);
OrderShipment.OrderProcessingMethods process3 =
new OrderShipment.OrderProcessingMethods(deliverorders.CalculateDiscount);
OrderShipment.OrderProcessingMethods process4 =
new OrderShipment.OrderProcessingMethods(deliverorders.AwordFreeGifts);
OrderShipment.OrderProcessingMethods process5 =
new OrderShipment.OrderProcessingMethods(deliverorders.GetOrderConfirmation);
- Before processing the order for normal customer, a delegate chain is formed by adding the delegate created on the previous step. Once the individual delegates are combined using the + operator, the result is stored in the
orderprocess
delegate. Now, the orderprocess
delegate holds the delegate
chain and we call it as Multicast delegate
. The multicast delegate
is ready, it is passed to the OrderShipment
class member function ProcessOrderShipment
. When a call is made on the multicast delegate
, the delegate
invokes all the functions currently in the chain. So for the Normal customer, we do not want to provide gift and/or discounts. Hence, those corresponding functions are not part of the delegate
chain. Also note that the chained functions are called in the same order they are added to the chain. Code and picture is given below:
Console.WriteLine("=====================================================");
Console.WriteLine("Process Normal Customer");
Console.WriteLine("=====================================================");
orderprocess = process1 + process2 + process5; deliverorders.ProcessOrderShipment(orderprocess,1000,1000);
- Next comes the VIP customer. As he is eligible for gift as well as discounts, we need to add the corresponding functions to the multicast delegate orderprocess. Note the current delegate order in the chain.
Process5
delegate is Order confirmation, which should be moved last. So process5
delegate removed from the chain, then process3
and process4
delegates are added to the chain. And finally, process5
delegate is put back before making the call to ProcessOrderShipment
. Note the usage of += operator. To add a delegate, you can use += operator. And to remove a delegate from the chain, you can use -= operator.
Console.WriteLine("=====================================================");
Console.WriteLine("Process VIP Customer");
Console.WriteLine("=====================================================");
orderprocess -= process5; orderprocess += process3; orderprocess += process4;
orderprocess += process5; deliverorders.ProcessOrderShipment(orderprocess,1001,1001);
- The last customer serviced is a regular customer. I hope no explanation is required now as you are familiar with multicast delegate or delegate chaining.
Console.WriteLine("=====================================================");
Console.WriteLine("Process Regular Customer");
Console.WriteLine("=====================================================");
orderprocess -= process4;
deliverorders.ProcessOrderShipment(orderprocess,1002,1002);
12. Custom Events
An event is kind of ‘Something Happened’. Some examples are the button got pressed; a check mark from the check box is removed. We all know that we call this kind of action as Events happening to those controls. So let us consider a form that has a button in it. And we all know that a button can be clicked. The user does the action of clicking a button and we do not know when that action will happen. In this situation, if we consider the application wants to know when the button got clicked and wants to say “Hi! There”. So what you think now. Somebody should say button is clicked and somebody should respond back to the event in our case saying just a “Hi”. I can hear you are saying “Not a big deal. Double click the button, the Development environment will bring me to a function, and write the code there that says Hi”. Well. If the Team Lead (Yes, the same guy who always bugs you) asks you “Hey! We have a class called Product Stock and it maintains the stock in hand in an integer variable. Can you expose an event say LowStock
so that the interested one will get a notification when the stock in hand goes below five?” What is the answer? “Yes”? OK, then you already know the Events very well. It is your choice whether you want to refresh it here or not. If your answer is “Hey. I know how to handle the UI events. I have not created my own event”, then this article is for you. Let us start.
13. Publish and Subscribe
If we go back to the button click on the form that says “Hi there”, there are some pieces of information we need to know.
- The
button
is placed on the form. The form is the container that holds the button, which is an UI Component.
- The
Button
class in .NET exposes an event called Click
. So button class publishes the event click.
- The
Form
class wants to know when the button
got clicked. So it subscribes for the published event Click
.
- When the
Button
is clicked, Subscriber for the event Click
on the button is notified. And there is handler code that says Hi when the notification is received. So the publish is nothing but exposing the event and subscribe is kind of getting the notification on the supplied function. Delegate and event are tightly coupled. We will see how when we are going through an example and at the same time you can answer the question for your team lead.
14. The Example
In this example, we have two classes. One is the ProductStock
class, which maintains current stock of the product. The other class is Counter
used by the billing counter computers in the retail shop. Let us say A customer comes to billing a counter, informs the product he want to purchase, pays the bill and goes to the stored room to receive the product. Each billing counter receives an notification when the product stock goes low. Consider the below picture before we move on:
- The
ProductStock
class publishes the event LowStock
Purchase
, Counter
, etc. classes subscribe to the Published event LowStock
ProductStock
sends a notification to the entire subscriber when the ProductStock
goes low. In our example, we are not going to implement Purchase
and Someother
class.
15. The ProductStock Class
- The
ProductStock
has two member variables. One to know the product name and other one is keep track of the current stock. The current stock gets reduced by the sales counter when a sale of the product is performed.
public string ProductName;
private int StockInHand;
- The class declares a multicast delegate
OnStockLow
that takes an Event
source object and EventArgs
object. Event
source is ProductStock
as it is going to raise the notification (Event
). EventArgs
is usually a class derived from it EventArgs
that can be used to pack the information related to the event. To keep this example simple, I have not derived any object from EventArgs
. Below is the declaration of Multicast delegate:
public delegate void OnStockLow(object sender, EventArgs e);
- Next, the event
StockLow
is declared. Note how the Delegate
is coupled with the Event
. It implies that notification handler function should return void
and receive object as first parameter and EventArgs
as second parameter. As it is a multicast delegate, you can use delegate chain of the above said functions. OK, now the Product
stock published the Event
. Below is the declaration of the event:
public event OnStockLow StockLow;
Note the syntax: public event
- The constructor of the
ProductStock
class initialized the members ProductName
and StockInHand
. Below is the code:
public ProductStock(string Name, int OpeningStock)
{
ProductName = Name;
StockInHand = OpeningStock;
}
- The
Counter
objects calls the ReduceStock
function when a sales is performed. This function reduces the current stock and notifies the subscriber of the LowStock
event when the current stock goes less than five. Below is the function implementation:
public void ReduceStock(int SalesDone)
{
StockInHand = StockInHand - SalesDone;
if (StockInHand < 5 )
{
EventArgs arg = new EventArgs();
StockLow(this, arg);
}
}
Note that in the above function call to StockLow(this,arg)
is known as raising the event or sending a notification. We are done with the ProductStock
class.
16. The Counter Class
- The
counter
class declares the member variable for the counter name and the constructor initializes the Name. The Sales
function takes the ProductStock
and number of product sold. It makes a call to the ReduceStock
after the sales. Below is the Implementation code:
public class Counter
{
private string CounterName;
public Counter(string Name)
{
CounterName = Name;
}
public void Sales(ProductStock prod, int howmuch)
{
Console.WriteLine("{0} Sold {1} numbers", prod.ProductName, howmuch);
prod.ReduceStock(howmuch);
}
- Counter class implements the notification handler for
StockLow
. Note that the arguments and the void
return type. Because, the rule is expected by the delegate OnLowStock
coupled with the event. Below is the handler:
public void LowStockHandler(object Sender, EventArgs e)
{
Console.WriteLine("Announcement on {0}: Stock of Product {1} gone Low",
CounterName , ((ProductStock) Sender).ProductName );
}
17. Client Code
Let us see how the client code works. Before that, a small refresh on what we did. The ProductStock
class exposes an event StockLow
and that event is coupled to OnStockLow
delegate. ReduceStock
function raises the StockLow
event when the product
stock goes below five. The counter class implements the notification handler LowStockHandler
to receive the notification. Where is the piece of code that links the LowStockHandler
to the StockLow
event? We will link that in the client code.
- First the client creates the two billing counter objects. Below is the code for billing counter:
Counter billing_counter1 = new Counter("Sindhu");
Counter billing_counter2 = new Counter("Ganga");
-
Next three ProductStock
objects are created. These products will be sold through two counters. Below is code:
ProductStock prod1 = new ProductStock("Godrej Fridge", 7 );
ProductStock prod2 = new ProductStock("Sony CD Player", 6);
ProductStock prod3 = new ProductStock("Sony DVD", 800);
-
Next, we subscribe to the event LowStock
published by the ProductStock
class by creating the Delegate
that points to the Notification
handler function implemented in the Counter
class. Below is the code:
prod1.StockLow += new ProductStock.OnStockLow(billing_counter1.LowStockHandler);
prod2.StockLow += new ProductStock.OnStockLow(billing_counter1.LowStockHandler);
prod1.StockLow += new ProductStock.OnStockLow(billing_counter2.LowStockHandler);
prod2.StockLow += new ProductStock.OnStockLow(billing_counter2.LowStockHandler);
- Everything setup. So let as start selling the products and see the notification we stock goes below
-
I recommend you to put a break point on the below piece of code and examine how the Events work. Below is the price code:
billing_counter1.Sales(prod1, 1);
billing_counter2.Sales(prod1, 2);
billing_counter2.Sales(prod3, 70);
billing_counter2.Sales(prod2, 1);
billing_counter1.Sales(prod2, 3);
billing_counter1.Sales(prod3, 5);
Output is shown below:

Closing Notes
- The attachment has three examples. When you unzip, it will deliver three solutions in three different zip files.
- All the three solutions are written in .NET 2003. If you have the latest version of the IDE, say yes to the Conversion Dialog.