Friday, November 19, 2010

[C#] - Delegates Part 1

Introduction


"Delegate" is a reference type just like another 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 organisation 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 the object created using the 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.
 

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 the 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 delegates. 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 a parameter and returns an integer to the caller. Later, you can create an instance of the delegate type GetTotalDelegate.

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.

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:

//001: A class for Staff
public class Staff
{
            //001_1: Member variables
            private int StaffId;
            private string StaffName;
            public int Salary;
            public int Bonus;
 
            //001_2: Constructor for Staff
            public Staff(int id, string name, int Salary, int bonus)
            {
                        StaffId = id;
                        StaffName = name;
                        this.Salary = Salary;
                        Bonus = bonus;
            }
 
            //001_3: String representation of staff
            public override string ToString()
            {
                        return string.Format("{0} - {1}", StaffName, StaffId);
            }
}
 

The Organization Class


This class has Array of staffs who forms the Organization.

1) First, a delegate is declared. The delegate name is GetTotalDelegate and it takes an array of staff as a parameter and returns an integer. Below is delegate:

//002_1: Delegate that Calculates and return the Total
public delegate int GetTotalDelegate(Staff[] staffs);

2) Next, two member variables are placed in this class. One is Array of staff and another one is for Name of the Organization.

//002_2: Other member variables
private Staff[] Staffs;
private string Org_Name;

3) The constructor will initialize the internal members. Constructor code is given below:

//002_3: Constructor for Organization
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; i++)
                        Staffs[i] = staffs[i];
 
            //002_3.2: Initialize other member variables
            Org_Name = Org_name ;
}

4) The Calculate_Total function takes the delegate of type GetTotalDelegate as a 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 return an integer. Here, we do not bother what is implemented by the function that came as the parameter in the form of the delegate. Below is the Function that receives function as parameter (Delegate) and returns an integer:

//002_4: Function that delegates the work of Calculating Total
public int Calculate_Total(GetTotalDelegate delegateRef)
{
            return delegateRef(Staffs);
}

5) 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:

//002_5: Diaplay all Staffs
public void DisplayStaffs()
{
            foreach(Staff staff in Staffs)
                        Console.WriteLine(staff);
}

Full Organization class is given below:

//002: Oraganization has Staffs for its Operation
public class Organization
{
            //002_1: Delegate that Calculates and return the Total
            public delegate int GetTotalDelegate(Staff[] staffs);
 
            //002_2: Other member variables
            private Staff[] Staffs;
            private string Org_Name;
 
            //002_3: Constructor for Organization
            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; i++)
                                    Staffs[i] = staffs[i];
 
                        //002_3.2: Initialize other member variables
                        Org_Name = Org_name ;
            }
 
            //002_4: Function that delegates the work of Calculating Total
            public int Calculate_Total(GetTotalDelegate delegateRef)
            {
                        return delegateRef(Staffs);
            }
 
            //002_5: Diaplay all Staffs
            public void DisplayStaffs()
            {
                        foreach(Staff staff in Staffs)
                                    Console.WriteLine(staff);
            }
 
}

The Calculate Utility Class


If a class has all static functions in, it will 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 this function signature maps the delegate we declared on the Organization class. That is both the functions receive Staff Array as a 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]:

//003: Utility Class for Making Calculation
public class Calculate
{
            //003_1: Helper function to Calculate Total Salary Expense
            public static int Total_Salary(Staff[] Staffs)
            {
                        int sum = 0;
                        foreach(Staff staff in Staffs)
                                    sum = sum + staff.Salary ;
                        return sum;
            }
           
            //003_2: Helper function to Calculate Total Bonus for All Staffs
            public static int Total_Bonus(Staff[] Staffs)
            {
                        int sum = 0;
                        foreach(Staff staff in Staffs)
                                    sum = sum + staff.Bonus ;
                        return sum;
            }
}

Delegate usage


Let us see how the user of the above classes uses the delegate. First instances of four Staff are created.

//Client 001: Create Staffs
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 staffs created. The Organization class will copy the array and holds it in the class array member variable Staffs.

//Client 002: Create Organization
Organization Org = new Organization("C# Corner", staff1, staff2, staff3, staff4 );
Org.DisplayStaffs();

Next, two delegate instances Salary_TotalBonus_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 need extra explanation, download the attached source code and examine exe file with ildasm. Look at the internal class GetTotalDelegate that is by the way internal for our Organization class.
 
//Client 003: Create the Delegates of same type pointing to different function
Organization.GetTotalDelegate Salary_Total = new Organization.GetTotalDelegate(Calculate.Total_Salary );
Organization.GetTotalDelegate Bonus_Total = new Organization.GetTotalDelegate(Calculate.Total_Bonus );

Once the delegates 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 point 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:

//Client 004: Now pass these delegates that is pointer to a function wrapped as a class GetTotalDelegate
//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 );

Closing Notes


Download the demo and examine the Exe using the ILDASM.
 
For Beginners [How do I]:

  1. Open the console window using Microsoft Visual Studio <Your Version> => Visual Studio Tools => Visual Studio <Your version> Command Prompt.
  2. Move to the directory where you extracted the Zip file, which has the Exe.
  3. Type ildasm <Name of the Exe>.exe
  4. Examine the to see how the statement public delegate int GetTotalDelegate(Staff[] staffs); converted by the compiler as internal class of type GetTotalDelegate

Note: The Example source code is created in Visual studio .net 2003 so that all latest versions can also use it. If you open the solution in the latest version just click the yes button for Convert dialog.


Source Code : Download

2 comments:

  1. Hi, I tried this example, it gives more understanding about the delegates. Can you tell me what was the logic behind the Calculate_Total function.Thankyou

    ReplyDelete
  2. Class Design Perspective:
    Ganesh, the Calculate_Total member Function takes GetTotalDelegate as parameter and makes a call to that function tied to the delegate parameter. So the designer of that class don't worry about implementation of Get Total.

    The User of the Class Perspective:
    They can call the Calculate_Total function with a delegate of type GetTotalDelegate. That means, they can have one more version of function expected by delegate (Takes Staff array, return int)and pass that to the claculate_total function.

    ReplyDelete

Leave your comment(s) here.

Like this site? Tell it to your Firend :)