Saturday, May 5, 2018

C# : What is delegates in C# and uses of delegates

Practical Use of Delegate in real time Application -1

Introduction

Many time we get questions like where can we use delegate ?  Give us a practical example .In fact I saw 2-3 similar questions in this forum so thought of writing this article.
In this age programmers are more interested in building dynamic and reusable component. There are scenarios where a normal function will not help to fulfil the purpose. Below are two important features in delegate which helps to make a component dynamic.
1-      1. It allows to inject code by the client(I will describe in this article)
2-      2. Multi-casting (I will describe in the next article)

Inject functions by client code.

Ok let's do a practical  example to make it clear. One important use of delegate is with the help of delegate  we can develop a component which client can use and inject their functionality to it. In the below example I have shown a UserRegistration Component. Client will call this component to register a user ,however it entirely depends on the client  to implement how they want to notify users about their registration via email or via SMS .

namespace RegistrationModule
{ 
    public delegate void NotifyUser(ContactDetail objContact); 
    class RegisterUser    {
        public void Register(Userinfo objUser, NotifyUser myFunc)        {
            //code to save the user details to the database
            // 
            myFunc(objUser.objContact);
        } 
    }

    class Userinfo    {
        public string name;        public int age;        public ContactDetail objContact;    }
    class ContactDetail    {
        public string email;        public string mobNo;    }
}

Client Code:
Lets say first client wants to send SMS to notify their user.So below will be their implementation.
I have given the comment to explain the code.
//Below function is having same signature as delegate NotifyUser and will be attached to the delegate.public void SendSMS(ContactDetail objContact)
        {
            string MobNo = objContact.mobNo;            //Add your code to send Registration success message to the user mobile number.
        }

        public void RegisterUser()
        {   //Create an Object of Userifo and fill the user details to it.            Userinfo obj = new Userinfo();           
            obj.name = "Satya Sahoo";
            obj.age = 29;
            ContactDetail objC = new ContactDetail();
            objC.mobNo = "977788888";
            objC.email = "esensahoo@sweetcrazyboy.net";
            obj.objContact = objC;
   //Create a delegate instance and attach the SendSMS() to it.            NotifyUser sendSMS = new NotifyUser(SendSMS);
            RegisterUser objReg = new RegisterUser();
//Invoke the register function with the Delegate Instance sendSMS

            objReg.Register(obj, sendSMS); 
        }

Now lets say the second client wants to notify the user via email:
public void SendEmail(ContactDetail objContact)
        {            string strEmail = objContact. email;
            //Add your code to send Registration success email to the user email address.
        }

        public void RegisterUser()
        {
            Userinfo obj = new Userinfo();           
            obj.name = "Satya Sahoo";
            obj.age = 29;
            ContactDetail objC = new ContactDetail();
            objC.mobNo = "977788888";
            objC.email = "esensahoo@sweetcrazyboy.net";
            obj.objContact = objC; 
            NotifyUser sendEmail = new NotifyUser(SendEmail); 
            RegisterUser objReg = new RegisterUser();
            objReg.Register(obj, sendEmail);
         }
That's it.Now client-1 user will get user registration success message as an SMS and client-2 will get user registration success message as an email.  Important thing to notice here is SendSMS and SendEmail is the code written by the client however it is invoked by the RegistrationModule what in other words we can say delegates allow us to inject functionality.
-----------------------------------------------------------------------------------------------------
What is a delegate?
  • It is a reference type.
  • It is a function pointer or it holds a reference (pointer) to a function (method).
  • It is type safe.
  • Delegates are mainly used for the event handling and the callback methods.
Explanation
  1. Let’s create a simple function to display the message, as shown below.
    1. public static void PrintMessage(string msg)  
    2.         {  
    3.             Console.WriteLine(msg);  
    4.         }  
  2. Now, create a delegate to refer this function, as shown below.

    To create a delegate, use the keyword delegate and remaining same as method signature.


    public delegate       void    PrintMsgFunctionDelgate(string Message);

    Here, this delegate has return type void and one string parameter. This delegate can be used to point to any function, which has a similar signature i.e., it can be used to point to any function, which has void return type and one string parameter.
  3. Now, let’s see how we will make this delegate point to the function PrintMessage.

    To do this, we have to create an instance of this delegate and this is where a delegate is similar to a class. To create an instance of a delegate, proceed as shown below.



    You can see the intellisence is telling that to pass the method name, which is having void return type and the string parameter, the function PrintMessage has the same signature, so I am passing it, as shown below.

    PrintMsgFunctionDelgate print = new PrintMsgFunctionDelgate(PrintMessage);
  4. To invoke that method, all we have to do is to invoke this delegate, as shown below. It is similar like function calls,
    1. print("welcome");  
    This delegate will call PrintMessage function indirectly.
If you are pointing to a function, which has a different signature, you will get a compiler error, as shown below.

In the example given below, my function return type is string and delegate return type is void.



Real time example

Let’s create a class employee
  1. class Employee  
  2.     {  
  3.         public int ID { get; set; }  
  4.         public string Name { get; set; }  
  5.         public int salary { get; set; }  
  6.         public float Experiance { get; set; }  
  7.   
  8.         public static void PromoteEmp(List<Employee> EmployeeList)  
  9.         {  
  10.             foreach (Employee  emp in EmployeeList )  
  11.             {  
  12.                 if(emp.Experiance>=5)//logic condition  
  13.                 {  
  14.                     Console.WriteLine(emp.Name + " promoted");  
  15.                 }  
  16.             }  
  17.         }  
  18.     }  
Now, I wanted to make this class reusable, but right now it is not. As you can see here, this class has a hard coded condition for promoting employee. Sometimes an employee will be promoted based on salary. For example, whosoever has a  salary more than 15,000. In this case, we have to go and edit these hard coded conditions. This is not the right way. We have to change this class, so that without changing the condition inside the class, they can promote the employees there. As per logic, we can use delegate.

Before using delegate, let’s call this class and see how the output will be

  1. public static void Main()  
  2.         {  
  3.             List<Employee> empl = new List<Employee>();  
  4.             empl.Add(new Employee() { ID = 101, Name = "Ravi", salary = 20000, Experiance = 3 });  
  5.             empl.Add(new Employee() { ID = 102, Name = "John", salary = 30000, Experiance = 5 });  
  6.             empl.Add(new Employee() { ID = 103, Name = "Mary", salary = 50000, Experiance = 8 });  
  7.             empl.Add(new Employee() { ID = 104, Name = "Mike", salary = 10000, Experiance = 2 });  
  8.             Employee.PromoteEmp(empl);  
  9.         }  
The output would be as shown below.



Now, let’s see how this PromoteEmp method inside class makes it reusable, using delegate.

  1. Create delegate that points to the function, which checks the condition and returns true or false by passing an Employee object, as shown below.

    delegate bool isPromote(Employee emp);
  2. Now, we will replace that, if the condition expression is with that delegate. For this, I am going to pass this delegate as a parameter to the function PromoteEmp and in if condition expression, I am going to call this delegate with an employee object, as shown below.
    1. public static void PromoteEmp(List<Employee> EmployeeList,isPromote IsEligible)  
    2.         {  
    3.             foreach (Employee  emp in EmployeeList )  
    4.             {  
    5.                 if(IsEligible(emp))//logic condition  
    6.                 {  
    7.                     Console.WriteLine(emp.Name + " Promoted");  
    8.                 }  
    9.             }  
    10.         }  
    Now, you can see that there is no hard coded logic to promote an employee. The logic is used when we call this method.

    The logic is written in a separate function, as shown below.
    1. Public static bool promote(Employee emp)  
    2.         {  
    3.             if (emp.Experiance >= 5)  
    4.             {  
    5.                 return true;  
    6.             }  
    7.             else  
    8.             {  
    9.                 return false;  
    10.             }  
    11. }  
    Here, you can write your own logic but it should return true or false.
Passing this logic and promote employee is shown below.
  1. Creating an instance of delegate
    1. isPromote pro = new isPromote(promote);   
  2. Calling PromoteEmp function with passing the employee list and the delegate is given below.
    1. Employee.PromoteEmp(empl, pro);  
    The full code is given below.
    1. public static void Main()  
    2.         {  
    3.             List<Employee> empl = new List<Employee>();  
    4.             empl.Add(new Employee() { ID = 101, Name = "Ravi", salary = 20000, Experiance = 3 });  
    5.             empl.Add(new Employee() { ID = 102, Name = "John", salary = 30000, Experiance = 5 });  
    6.             empl.Add(new Employee() { ID = 103, Name = "Mery", salary = 50000, Experiance = 8 });  
    7.             empl.Add(new Employee() { ID = 104, Name = "Mike", salary = 10000, Experiance = 2 });  
    8.   
    9.             isPromote pro = new isPromote(promote);  
    10.             Employee.PromoteEmp(empl, pro);  
    11.         }  
    12.   
    13.         public static bool promote(Employee emp)  
    14.         {  
    15.             if (emp.Experiance >= 5)  
    16.             {  
    17.                 return true;  
    18.             }  
    19.             else  
    20.             {  
    21.                 return false;  
    22.             }  
    23.         }  
    24.     
    25.     }  
    26.      
    27.     delegate bool isPromote(Employee emp);  
    28.     class Employee  
    29.     {  
    30.         public int ID { get; set; }  
    31.         public string Name { get; set; }  
    32.         public int salary { get; set; }  
    33.         public float Experiance { get; set; }  
    34.   
    35.          
    36.         public static void PromoteEmp(List<Employee> EmployeeList,isPromote IsEligible)  
    37.         {  
    38.             foreach (Employee  emp in EmployeeList )  
    39.             {  
    40.                 if(IsEligible(emp))//logic condition  
    41.                 {  
    42.                     Console.WriteLine(emp.Name + " Promoted");  
    43.                 }  
    44.             }  
    45.         }  
    46. }  
There are two types of delegates, which are given below.
  • Unicast deligate

    This delegate refers to only one method, as shown in the above examples.
  • Multicast deligate

    This delegate can refer to more than one method. This delegate maintains a list of the methods.
    Example,
    1. delegate void strDele(string str); //declare  
    2. strDele delobj += new strDele (uppercasestr); //method reference 1  
    3. delobj += new strDele (Lowercasestr); //method reference 2   
    4. delobj(“Welcome”); //invoking the multicast delegate  

Note
An instance of a delegate can refer to static as well as instance methods. The signature of function declaration and the delegate declaration should match.
----------------------------------------------------------------------------------------------------------
C# delegates are same as pointers to functions, in C or C++. A delegate Object is a reference type variable that use to holds the reference to a method. The reference can be changed at runtime which is hold by an object of delegate, a delegate object can hold many functions reference which is also known as Invocation List that refers functions in a sequence FIFO, we can new functions ref in this list at run time by += operator and can remove by -= operator. 

Delegates are especially used for implementing events and the call-back methods. All delegates are implicitly derived from the System.Delegate class.

Let’s see how to use Delegate with Example:

ASP.NET


There are 6 important uses of delegates:-
1. Abstract and encapsulate a method (Anonymous invocation)
This is the most important use of delegates; it helps us to define an abstract pointer which can point to methods and functions. The same abstract delegate can be later used to point to that type of functions and methods. In the previous section we have shown a simple example of a maths class. Later addition of new algorithm functions does not affect the UI code.

2. Callback mechanismMany times we would like to provide a call back mechanism. Delegates can be passed to the destination and destination can use the same delegate pointer to make callbacks.

3. Asynchronous processingBy using ‘BeginInvoke’ and ‘EndInvoke’ we can call delegates asynchronously. In our previous section we have explained the same in detail.

4. Multicasting - Sequential processing Some time we would like to call some methods in a sequential manner which can be done by using multicast delegate. This is already explained in the multicast example shown above.

5. Events - Publisher subscriber modelWe can use events to create a pure publisher / subscriber model.



Difference between delegates and C++ pointer





Abstraction problems of methods and functions
 

Before we move ahead and we talk about delegates let’s try to understand what problem does delegate solve. Below is a simple class named ‘ClsMaths’ which has a simple ‘Add’ function. This class ‘ClsMaths’ is consumed by a simple UI client. Now let’s say over a period of time you add subtraction functionality to the ‘ClsMaths’ class, your client need to change accordingly to accommodate the new functionality.
In other words addition of new functionality in the class leads to recompiling of your UI client.
x`
In short the problem is that there is a tight coupling of function names with the UI client. So how can we solve this problem?. Rather than referring to the actual methods in the UI / client if we can refer an abstract pointer which in turn refers to the methods then we can decouple the functions from UI.
Later any change in the class ‘ClsMath’ will not affect the UI as the changes will be decoupled by the Abstract pointer. This abstract pointer can be defined by using delegates. Delegates define a simple abstract pointer to the function / method.
Ok, now that we have understood the tight coupling problem and also the solution, let’s try to understand how we can define a simple delegate.

How to declare a delegate?
 

To implement a delegate is a four step process declare, create, point and invoke.
The first step is to declare the delegate with the same return type and input parameters. For instance the below function ‘add’ has two input integer parameters and one integer output parameter.
private int Add(int i,int y)
{
return i + y;
}


So for the above method the delegate needs to be defined in the follow manner below. Please see the delegate keyword attached with the code.
// Declare delegate
public delegate int PointetoAddFunction(int i,int y);

The return type and input type of the delegate needs to be compatible, in case they are not compatible it will show the below error as shown in the image below.

The next step (second step) is to create a delegate reference.
// Create delegate reference
PointetoAddFunction myptr = null;
The third step is to point the delegate reference to the method, currently we need to point the delegate reference to the add function.
// Point the reference to the add method
myptr = this.Add;

Finally we need to invoke the delegate function using the “Invoke” function of the delegate.
// Invoke the delegate
myptr.Invoke(20, 10)
Below figure sums up how the above four step are map with the code snippet.

Solving the abstract pointer problem using delegates 
 

In order to decouple the algorithm changes we can expose all the below arithmetic function through an abstract delegate.
So the first step is to add a generic delegate which gives one output and takes two inputs as shown in the below code snippet.
public class clsMaths
{
public delegate int PointerMaths(int i, int y);
}

The next step is to expose a function which takes in operation and exposes an attached delegate to the UI as shown in the below code snippet.
public class clsMaths
{
public delegate int PointerMaths(int i, int y);

public PointerMaths getPointer(int intoperation)
{
PointerMaths objpointer = null;
if (intoperation == 1)
{
objpointer = Add;
}
else if (intoperation == 2)
{
objpointer = Sub;
}
else if (intoperation == 3)
{
objpointer = Multi;
}
else if (intoperation == 4)
{
objpointer = Div;
}
return objpointer;
}
}

Below is how the complete code snippet looks like. All the algorithm functions i.e. ‘Add’ , ‘Sub’ etc are made private and only one generic abstract delegate pointer is exposed which can be used to invoke these algorithm function.
public class clsMaths
{
public delegate int PointerMaths(int i, int y);

public PointerMaths getPointer(int intoperation)
{
PointerMaths objpointer = null;
if (intoperation == 1)
{
objpointer = Add;
}
else if (intoperation == 2)
{
objpointer = Sub;
}
else if (intoperation == 3)
{
objpointer = Multi;
}
else if (intoperation == 4)
{
objpointer = Div;
}
return objpointer;
}

private int Add(int i, int y)
{
return i + y;
}
private int Sub(int i, int y)
{
return i - y;
}
private int Multi(int i, int y)
{
return i * y;
}
private int Div(int i, int y)
{
return i / y;
}
}

So at the client side the calls becomes generic without any coupling with the actual method names like ‘Add’ , ‘Sub’ etc.
int intResult = objMath.getPointer(intOPeration).Invoke(intNumber1,intNumber2);

Multicast delegates
 

In our previous example we have see how we can create a delegate pointer to a function or method. We can also create a delegate which can point to multiple functions and methods. If we invoke such delegate it will invoke all the methods and functions associated with the same one after another sequentially.
Below is the code snippet which attaches 2 methods i.e. method1 and method2 with delegate ‘delegateptr’. In order to add multiple methods and function we need to use ‘+=’ symbols. Now if we invoke the delegate it will invoke ‘Method1’ first and then ‘Method2’. Invocation happen in the same sequence as the attachment is done.
// Associate method1
delegateptr += Method1;
// Associate Method2
delegateptr += Method2;
// Invoke the Method1 and Method2 sequentially
delegateptr.Invoke();

So how we can use multicast delegate in actual projects. Many times we want to create publisher / subscriber kind of model. For instance in an application we can have various error logging routine and as soon as error happens in a application you would like to broadcast the errors to the respective components.

Simple demonstration of multicast delegates
 

In order to understand multicast delegates better let’s do the below demo. In this demo we have ‘Form1’, ‘Form2’ and ‘Form3’. ‘Form1’ has a multicast delegate which will propagate event to ‘Form2’ and ‘Form3’.
At the form level of ‘Form1’ (this form will be propagating events to form2 and form3) we will first define a simple delegate and reference of the delegate as shown in the code snippet below. This delegate will be responsible for broadcasting events to the other forms.
// Create a simple delegate
public delegate void CallEveryOne();

// Create a reference to the delegate
public CallEveryOne ptrcall=null;
// Create objects of both forms

public Form2 obj= new Form2();
public Form3 obj1= new Form3();

In the form load we invoke the forms and attach ‘CallMe’ method which is present in both the forms in a multicast fashion ( += ).
private void Form1_Load(object sender, EventArgs e)
{
// Show both the forms
obj.Show();
obj1.Show();
// Attach the form methods where you will make call back
ptrcall += obj.CallMe;
ptrcall += obj1.CallMe;
}

Finally we can invoke and broadcast method calls to both the forms.
private void button1_Click(object sender, EventArgs e)
{
// Invoke the delegate
ptrcall.Invoke();
}

Problem with multicast delegates – naked exposure
 

The first problem with above code is that the subscribers (form2 and form3) do not have the rights to say that they are interested or not interested in the events. It’s all decided by ‘form1’.
We can go other route i.e. pass the delegate to the subscribers and let them attach their methods if they wish to subscribe to the broadcast sent by ‘form1’. But that leads to a different set of problems i.e. encapsulation violation.
If we expose the delegate to the subscriber he can invoke delegate, add his own functions etc. In other words the delegate is completely naked to the subscriber.

Events – Encapsulation on delegates
 

Events help to solve the delegate encapsulation problem. Events sit on top of delegates and provide encapsulation so that the destination source can only listen and not have full control of the delegate object.
Below figure shows how the things look like:-
• Method and functions are abstracted /encapsulated using delegates
• Delegates are further extended to provide broadcasting model by using multicast delegate.
• Multicast delegate are further encapsulated using events.

Implementing events
 

So let’s take the same example which we did using multicast delegates and try to implement the same using events. Event uses delegate internally as event provides higher level of encapsulation over delegates.
So the first step in the publisher (‘Form1’) we need to define the delegate and the event for the delegate. Below is the code snippet for the same and please do notice the ‘event’ keyword.
We have defined a delegate ‘CallEveryOne’ and we have specified an event object for the delegate called as ‘EventCallEveryOne’.
public delegate void CallEveryone();
public event CallEveryone EventCallEveryOne;

From the publisher i.e. ‘Form1’ create ‘Form2’ and ‘Form3’ objects and attach the current ‘Form1’ object so that ‘Form2’ and ‘Form3’ will listen to the events. Once the object is attached raise the events.
Form2 obj = new Form2();
obj.obj = this;
Form3 obj1 = new Form3();
obj1.obj = this;
obj.Show();
obj1.Show();
EventCallEveryOne();

At the subscriber side i.e. (Form2 and Form3) attach the method to the event listener.
obj.EventCallEveryOne += Callme;

This code will show the same results as we have got from multicast delegate example.

Difference between delegates and events
 

So what’s really the difference between delegates and events other than the sugar coated syntax of events. As already demonstrated previously the main difference is that event provides one more level of encapsulation over delegates.
So when we pass delegates it’s naked and the destination / subscriber can modify the delegate. When we use events the destination can only listen to it.

Delegates for Asynchronous method calls
 

One of the other uses of delegates is asynchronous method calls. You can call methods and functions pointed by delegate asynchronously.
Asynchronous calling means the client calls the delegate and the control is returned back immediately for further execution. The delegate runs in parallel to the main caller. When the delegate has finished doing his work he makes a call back to the caller intimating that the function / subroutine has completed executing.
To invoke a delegate asynchronously we need call the ‘begininvoke’ method. In the ‘begininvoke’ method we need to specify the call back method which is ‘CallbackMethod’ currently.
delegateptr.BeginInvoke(new AsyncCallback(CallbackMethod), delegateptr);

Below is the code snippet for ‘CallbackMethod’. This method will be called once the delegate finishes his task.
static void CallbackMethod(IAsyncResult result)
{
int returnValue = flusher.EndInvoke(result);
}

No comments:

Post a Comment