Best explanation for why to use delegate in dotnet applications

+1 vote
asked Jul 14, 2013 in C#.NET by Nancy

It is nowhere I have found or not understood the reason for why should I need to use delegate? What kind of scenario may force be to deliberately use delegate in my application? Why it is so difficult to understand this delegates and events concept? I have gone through lot of question and answers in this website and I really liked because it really explain the concepts in-depth. So, I really wonder, if you can post me some good answer for this question as well. Thanks in advance.

Share

2 Answers

+2 votes
answered Jul 14, 2013 by Aadhira (1,213 points)
selected Jul 15, 2013 by administrator
 
Best answer

Delegate is really special type of class. Yes, it is a class. That is what it is going to be once you compile your code. It is not just a class. It is a class which is derived from MultiCastDelegate base class. Once you compile the code, dotnet framework will convert the delegate to class and also, it will derive that class from a base class called "MultiCastDelegate".

So why do we need this special class? The regular class cannot do the job which a delegate can do.

Look at the below series of scenario.

public class Program
{
  static void Main(string[] args)
  {
    DataProcessor dataProcessor = new DataProcessor();
    dataProcessor.Process(new List<int> { 1, 2, 3, 4 });
    Console.Read();
  }
}

public class DataProcessor
{
 public void Process(List<int> dataCollection)
 {
    for (int i = 0; i < dataCollection.Count; i++ )
    {//long running code}
  }
}

I have a class "DataProcessor" that has a method "Process". If I pass data to that method, it will process that.

Scenario 1:

Consider a scenario where data collection is very huge and it may take very long time to process. If I do not get any notification regarding which item is processed currently, then I may be sitting idle without knowing what is going on. Right?

So, to get the notification back, may be I can add one method in "Program" class and call that method from DataProcessor and update like below.

public class Program
{
    static void Main(string[] args)
    {
        DataProcessor dataProcessor = new DataProcessor();
        dataProcessor.Process(new List<int> { 1, 2, 3, 4 });
        Console.Read();
    }
    public static void DataProcessUpdateHandler(int index)
    {
        Console.WriteLine("Update received for Item " + index.ToString());
    }
}

public class DataProcessor
{
    public void Process(List<int> dataCollection)
    {
        for (int i = 0; i < dataCollection.Count; i++ )
        {
            //long running code
            Program.DataProcessUpdateHandler(i);
        }
    }
}

Okay, the issue is resolved for the given scenario. Now, lets add a complexity.

Scenario 2:

I have two different collection of data. When I pass one collection, I need to receive the update in one particular method and if I pass the other collection, then I need the status update on the other method.

Yes, it is still very simple. Because I can pass some kind of indicator to inform the DataProcessor class to tell, which update method should be called. Then DataProcessor can call that particular update method like below.

public class Program
{
    static void Main(string[] args)
    {
        dataProcessor = new DataProcessor();
        dataProcessor.Process(new List<int> { 1, 2, 3, 4 }, "DataProcessUpdateHandler1");
        dataProcessor.Process(new List<int> { 5, 6, 7, 8 }, "DataProcessUpdateHandler2");
        Console.Read();
    }

    public static void DataProcessUpdateHandler1(int index)
    {
        Console.WriteLine("Update received by DataProcessUpdateHandler1 for Item " + index.ToString());
    }

    public static void DataProcessUpdateHandler2(int index)
    {
        Console.WriteLine("Update received by DataProcessUpdateHandler2 for Item " + index.ToString());
    }
}
public class DataProcessor
{
        public void Process(List<int> dataCollection, string updateHandledBy)
        {
            for (int i = 0; i < dataCollection.Count; i++ )
            {
                System.Threading.Thread.Sleep(1000);
                if (updateHandledBy == "DataProcessUpdateHandler1")
                    Program.DataProcessUpdateHandler1(i);
                else
                    Program.DataProcessUpdateHandler2(i);
            }
        }
}

Perfect, we resolved that issue as well.

Scenario 3:

Now the scenario is, I have hundreds of data list. The DataProcess must update the status to hundred different update handler method based on the data list passed.

Now it is interesting. Isn't it? If we need to do this, then we need to pass the method unique identifies to the "Process" method of the DataProcess class, and I need to write a switch case to handle each update method. I don't think this is a correct approach.

Instead of sending the unique identifier to the "Process" method, it would be really wonderful if we can pass the Update Method itself as the parameter to the Process method and let the Process method call the method whatever. If that is the case then no need to write the switch case, isn't it?

Reason for calling the delegate as Pointer to Function:

Yes that's right. That would really wonderful if possible. The truth is, it is possible and that is the purpose of delegates. Delegates can hold the function itself. Actually, the address of the function. Address of the function is good enough the call the function. That is the reason, we call the delegate as pointer to the function.

Since we know what is the purpose delegate, let see how do we use it to resolve our issue in the last scenario.

Okay, delegate can hold the address of the function. But how to declare the delegate to hold our update method.

This is very simple. If you have a toy with the shape Square, can you put it inside a triangle box? (Please don't say that if the triangle box is big enough to hold that square box) I am trying something to explain that, the shape and size should be same. Like that,

If you want a delegate to hold your Update method, then both Update method and the delegate signatures must match.

So, I am going to copy one of the update method signature.

public static void DataProcessUpdateHandler1(int index);

Now I will convert this method into delegate. First remove the static keyword, because delegate no need to be static.

public void DataProcessUpdateHandler1(int index);

Now add a delegate keyword in the above line.

public delegate void DataProcessUpdateHandler1(int index);

That's it. You have the delegate to hold your update function. If you want, you have rename the Delegate Name. "DataProcessUpdateHandler1" is the delegate Name (Like class name). So, if you want to create variable for this delegate same like a class, you will create delegate instance.

lets rename it now. Usually, the delegate naming convention would be, the action what it does with "Handler" suffix at the end. So, everything is fine in the name except then numeric "1". I will remove that.

public delegate void DataProcessUpdateHandler(int index);

Now use this delegate in our program and call the methods dynamically. Just for an example, instead of having hundred list, I have it with 3 list. I have replaced the string parameter of "Process" method with the parameter of type delegate what we created.

Please continue reading the next answer where I have explained the remains part of this answer.

+1 vote
answered Jul 14, 2013 by Aadhira (1,213 points)

Answer continuation of previous part.

class Program
    {
        static void Main(string[] args)
        {
            dataProcessor = new DataProcessor();
            dataProcessor.Process(new List<int> { 1, 2, 3, 4 }, DataProcessUpdateHandler1);
            dataProcessor.Process(new List<int> { 5, 6, 7, 8 }, DataProcessUpdateHandler2);
            dataProcessor.Process(new List<int> { 9, 10, 11, 12 }, DataProcessUpdateHandler3);
            Console.Read();
        }

        public static void DataProcessUpdateHandler1(int index)
        {
            Console.WriteLine("Update received by DataProcessUpdateHandler1 for Item " + index.ToString());
        }

        public static void DataProcessUpdateHandler2(int index)
        {
            Console.WriteLine("Update received by DataProcessUpdateHandler2 for Item " + index.ToString());
        }
        public static void DataProcessUpdateHandler3(int index)
        {
            Console.WriteLine("Update received by DataProcessUpdateHandler3 for Item " + index.ToString());
        }
    }

    public delegate void DataProcessUpdateHandler(int index);

    public class DataProcessor
    {
        public void Process(List<int> dataCollection, DataProcessUpdateHandler updateHandledBy)
        {
            for (int i = 0; i < dataCollection.Count; i++ )
            {
                //Long running process
            }
        }
    }

Still I have not fixed the issue, because nowhere I have call the delegate. So, how to use the delegate to call the method?

Delegates can be called the same we call the regular methods.I have add code in "Process" method to call the delegate whatever is being passed.

public class DataProcessor
{
    public void Process(List<int> dataCollection, DataProcessUpdateHandler updateHandledBy)
    {
        for (int i = 0; i < dataCollection.Count; i++ )
        {
            //Long running process
            updateHandledBy(i);
        }
    }
}

So, finally we resolved the issue in Scenario 3. Lets take the scenario to next level.

Scenario 4:

Let say, I have only one data collection. Now when I start processing the data, I need to get the update in two different method which are in 2 different classes.

public class Subscriber1
{
    public void DataProcessUpdateHandler(int index)
    {
        Console.WriteLine("Update received by Subscriber1 for Item " + index.ToString());
    }
}

public class Subscriber2
{
    public void DataProcessUpdateHandler(int index)
    {
        Console.WriteLine("Update received by Subscriber2 for Item " + index.ToString());
    }
}

As per the previous example, I can pass the function name to the "Process" method. But, the Process method has only one delegate parameter. In our previous scenario, we had different data list and we called only one method for each data list. But now, we have single data list, still we want to update the status to multiple methods and that too in multiple classes.

How do we pass, both the methods in to a single parameter? Do we need to create a List as a parameter type in the "Process" method like below?

public void Process(List<int> dataCollection, List<DataProcessUpdateHandler> updateHandledBy)
{
    //long running process
}

Definitely not. Delegates are Super star, because Single Delegate itself can hold more than one method at a time and if you call the "updateHandledBy", it will automatically call all the methods passed in the single parameter. So, we want to know how is it possible and how to do that right?

Absolutely. I think, in the beginning of this article, I have told that delegate is the special class and it is derived from "MultiCastDelegate" base class. That base class is what the reason for delegate able to hold reference of multiple methods.

To do this, we want to modify the existing code.

First we will modify the DataProcess class as below. What I have done is, removed the delegate parameter from the Process method and declared as a public variable in the same class. And then, used that variable same as earlier to invoke the Update method.

public class DataProcessor
{
    public DataProcessUpdateHandler updateHandledBy = null;

    public void Process(List<int> dataCollection)
    {
        for (int i = 0; i < dataCollection.Count; i++ )
        {
            //Long running code
            if (updateHandledBy != null)
                updateHandledBy(i);
        }
    }
}

Below will be the code in Program class. Here I have used a special syntax to add multiple methods into a single delegate.

dataProcessor.updateHandledBy += subscriber1.DataProcessUpdateHandler;
dataProcessor.updateHandledBy += subscriber2.DataProcessUpdateHandler;

"+=" operator is used to add the Method to the delegate. Same like that, if you want to remove the method from the delegate, then you can use "-=" like below

dataProcessor.updateHandledBy -= subscriber1.DataProcessUpdateHandler;

The above code will remove only the "DataProcessUpdateHandler" method of _subscriber1 instance.

public class Program
{
    static DataProcessor dataProcessor = null;

    static void Main(string[] args)
    {
        dataProcessor = new DataProcessor();
        Subscriber1 subscriber1 = new Subscriber1();
        Subscriber2 subscriber2 = new Subscriber2();
        dataProcessor.updateHandledBy += subscriber1.DataProcessUpdateHandler;
        dataProcessor.updateHandledBy += subscriber2.DataProcessUpdateHandler;
        dataProcessor.Process(new List<int> { 1, 2, 3, 4 });
        Console.Read();
    }
}

It is a best practice to do null check always before we call the delegate. If none of the methods are attached with the delegate, then it will through exception. That's the reason I did null check on "updateHandledBy"

I hope, now you understand why we need delegate and how to use it in .net framework.

Your answer

Preview

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.
Anti-spam verification:
To avoid this verification in future, please log in or register.
site design / logo / content © 2013 - 2015 pinfaq.com
...