Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Delegate Tutorial for Beginners

0.00/5 (No votes)
28 Jul 2013 2  
Delegate tutorial for beginners.

Introduction

This is one of the articles from the series: "Delegate tutorial for beginners". This tutorial demonstrates how to create and use simple delegates and the different types.

Content

  • What is Delegate type?
  • What advantage does delegate makes over method invocation?
  • What is a Multicast Delegate?
  • What is the relation between Event and Delegate?
  • What is an Asynchronous Delegate?

What is Delegate type?

Delegate is a reference type, it references to a method having the same signature as the delegate.

Creation of a delegate is a three step process:

  1. Define the Delegate
  2. Create an object of the Delegate and assign a method to it
  3. Invoke the Delegate

1. Define the Delegate

Following is the way to define a delegate:

delegate returntype delegateName (parameter); 

[Return type and parameter define the signature of the Delegate.]

An object of the Delegate allows the programmer to encapsulate a reference to a method. We can assign any method to a Delegate object having the same signature as that of the delegate.

2. Create an Object of the Delegate and assign a method to it

We define the Delegate object as follows:

delegateName objectOfDelegate=null; 

We can assign a reference of the method to the delegate object as follows:

objectOfDelegate=functionName;

Note: The signature of the function and Delegate must be the same.

3. Invoke the Delegate

You can invoke a Delegate by using the Invoke method of the Delegate:

objectOfDelegate.Invoke(parameterValue); 

Invocation of the Delegate is the same as the invocation of the method that is encapsulate in the Delegate.

Let’s take a sample example. Consider a function that accepts two numbers and adds them.

Let’s create a Delegate that points to an Add function.

1. Define the Delegate

delegate void PointToAdd(int numberOne,int numberTwo); 

2. Create an object of the Delegate and assign a method to it

PointToAdd pointToAdd = AddTwoNumber;

3. Invoke a Delegate

pointToAdd.Invoke(10, 20); 

Now let’s look at a complete sample at once.

class DelegateDemo1
{ 
    delegate void PointToAdd(int numberOne,int numberTwo);
    static void Main(string[] args)
    {
        PointToAdd pointToAdd = AddTwoNumber;
        pointToAdd.Invoke(10, 20);
        Console.Read();
    }
    static void AddTwoNumber(int numberOne,int numberTwo)
    {
        Console.WriteLine((numberOne+numberTwo).ToString());
    }
}  

Invocation of the Delegate is the same as the invocation of the method that is encapsulate by the Delegate. So is it true that we can replace the Delegate with a simple method invocation?

What advantage does a Delegate have over a method invocation?

The developer can assign a method to a Delegate during runtime. Do you remember pointers in C? A Delegate is nothing but a pointer to a function. When developers are not aware which function to call or a function call is based on a condition, they can choose a delegate to call the function during run time.

Let’s take sample example. Similar to our above example we will create functions for adding, subtracting, division, and multiplication of two numbers.

We give the choice to the user to enter two numbers and a string, adds for addition, subs for subtraction, and div for division of two numbers.

Let’s define the Delegate that supports a simple math operation of two numbers.

public delegate void PointToMathsOperation(int numberOne, int numberTwo);
PointToMathsOperation pointToMathsOperation = null;   

We have assigned the Delegate to null. We will assign a method to the Delegate dynamically or at run time. We have the following methods matching the delegate signature.

private void Add(int numberOne, int numberTwo)
{
    Console.WriteLine((numberOne + numberTwo).ToString());
}

private void Sub(int numberOne, int numberTwo)
{
    Console.WriteLine((numberOne - numberTwo).ToString());
}

private void Div(int numberOne, int numberTwo)
{
    Console.WriteLine((numberOne / numberTwo).ToString());
}

private void Mul(int numberOne, int numberTwo)
{
    Console.WriteLine((numberOne * numberTwo).ToString());
}

Let's write a method that will assign a method to the Delegate based on the user choice and return the object of the Delegate.

public PointToMathsOperation Operation(string operationName)
{
    switch (operationName)
    {
        case "Add":
            pointToMathsOperation = Add;
            return pointToMathsOperation;
        case "Sub":
            pointToMathsOperation = Sub;
            return pointToMathsOperation;
        case "Div":
            pointToMathsOperation =Mul;
            return pointToMathsOperation;
        case "Mul":
            pointToMathsOperation = Div;
            return pointToMathsOperation;
    }
    return pointToMathsOperation;
}

Let's take a compete look at what we have done so far. We have defined a Math class as follows:

public class Math
{
    public delegate void PointToMathsOperation(int numberOne, int numberTwo);
    PointToMathsOperation pointToMathsOperation = null;
    public PointToMathsOperation Operation(string operationName)
    {
        
        switch (operationName)
        {
            case "Add":
                pointToMathsOperation = Add;
                return pointToMathsOperation;
            case "Sub":
                pointToMathsOperation = Sub;
                return pointToMathsOperation;
            case "Div":
                pointToMathsOperation =Mul;
                return pointToMathsOperation;
            case "Mul":
                pointToMathsOperation = Div;
                return pointToMathsOperation;
        }
        return pointToMathsOperation;
    }
    private void Add(int numberOne, int numberTwo)
    {
        Console.WriteLine((numberOne + numberTwo).ToString());
    }
    private void Sub(int numberOne, int numberTwo)
    {
        Console.WriteLine((numberOne - numberTwo).ToString());
    }
    private void Div(int numberOne, int numberTwo)
    {
        Console.WriteLine((numberOne / numberTwo).ToString());
    }
    private void Mul(int numberOne, int numberTwo)
    {
        Console.WriteLine((numberOne * numberTwo).ToString());
    }
} 

Let's instantiate the Math class and call the method Operation that returns the Delegate object. We can invoke the Delegate by passing two numbers to it as per the delegate signature. 

public class DelegateDemo2
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Please enter two number:-");
        var numberOneString = Console.ReadLine();
        var numberTwoString = Console.ReadLine();
        Console.WriteLine("Please enter operation to perform like add,mul,sub and div :-");
        var choice = Console.ReadLine();
        int numberOne, numberTwo;
        Math math = new Math();
        int.TryParse(numberOneString,out numberOne);
        int.TryParse(numberTwoString,out numberTwo);
        math.Operation(choice).Invoke(numberOne,numberTwo);
        Console.ReadLine();
    }
} 

What is a Multicast Delegate?

Multicast Delegate is an extension of a Delegate. You can assign multiple methods to a Delegate object and invoke them during Delegate invocation. Methods get invoked in the same hierarchy in which they get assigned to the Delegate object.

Let’s take a simple example. Consider a system that will broadcast messages when it fails doing transactions. The system will broadcast a message to the Admin via SMS on mobile, email on mailbox, and message on fax. For simplicity we will build a console application which accepts a string from the user, any string content other than “Pass” assumes that the transaction has failed, and we broadcast a message to the admin using a Multicast Delegate.

Let’s build a BroadCast class that contains a method to transmit a message to the admin via SMS on mobile, email on mailbox, and message on fax.

/// <summary>
/// broadcast done.
/// </summary>
public class BroadCast
{
    public void ViaMessage()
    {
        Console.WriteLine("Message Send to mobile.");
    }

    public void ViaEmail()
    {
        Console.WriteLine("Email send to mailbox.");
    }

    public void ViaFax()
    {
        Console.WriteLine("Message send via fax.");
    }
} 

Our system can broadcast a message using the above media. Let’s build a system that will broadcast a message when it fails using a Multicast Delegate.

/// <summary>
/// Multicast Delegate
/// </summary>
public class DelegateDemo3
{
    delegate void SendMessage();

    public static void Main(string[] args)
    {
        BroadCast broadCast=new BroadCast();
        SendMessage sendMessage = broadCast.ViaEmail;
        sendMessage += broadCast.ViaFax;
        sendMessage += broadCast.ViaMessage;
        Console.WriteLine("Enter trascation suncess.");
        var result = Console.ReadLine();
        if (result.Equals("Pass"))
        {
            Console.WriteLine("Successfull trasaction!");
        }
        else
        {
            Console.WriteLine("Transaction fail !");
            sendMessage.Invoke();
        }
        Console.ReadLine();
    }
}

What is the relation between an Event and a Delegate?

Consider a publisher who published something and there are subscribers who wait for the publisher to publish so they can start their work. There is actually no dependency between the subscriber and publisher. The subscriber will do its work independent of the publisher, they are independent agents but the subscriber needs an alert from the publisher to start its activity.

Let us relate this with our Multicast Delegate where our delegate is the publisher. It publishes the event broadcast message where our subscriber has complete functionality to broadcast a message on fax, cell, and email but they only want to perform their activity when the publisher publishes the broadcast event.

So we have our multicast delegate as our publisher and subscriber model.

Good enough but as we have stated earlier, delegates are pointers to functions, i.e., they reference to a function. We can set a reference as null, i.e., we can set a pointer as null also.

In our case the subscriber can null the publisher. The Subscriber can attach a method to the publisher object.

But we have said that the subscriber and publisher are independent agents, i.e., there is a decoupling between the subscriber and publisher. So we are breaking the rule of the subscriber publisher model. In order to maintain this model we can use the event which sits on top of the Delegate and encapsulates the Delegate.

Events are nothing but alerts to an action. If we performing an action of button click then we are raising the click event that just tells that button click is done. It does not enforce any rule on what needs to be done when the button is clicked. The event is an alert to the external stimuli or action on an object in object oriented programming. The subscriber can only listen to an event.

Consider a scenario where a group of people are subscribers interested to attend the meeting after a predefined time. They want an alert from the publisher after each predefined interval of time so they can sync their own activity accordingly.

Let’s build a UI that will accept two numbers. The first number indicates the time remaining for a meeting and the second indicating the alert wanted after the time interval periods.

If we select time remaining for meeting as 12 and want alert after time interval of 2, then we get an alert every 2 seconds for the meeting so will get a total of five alerts.

Let’s start building the above requirement. We will start with defining our Delegate.

public delegate void TimeReachedEventHandler(object sender, TimeReachedEventArgs e); 

This delegate will encapsulate any method that returns void and takes two parameters. The first parameter is an object that represents the sender (the object raising the event), and the second parameter is an object of type TimeReachedEventArgs, derived from EventArgs, that will contain useful information for anyone interested in this event.

TimeReachedEventArgs is defined as follows:

public class TimeReachedEventArgs : EventArgs
{
    private int _reached;
    public TimeReachedEventArgs(int num)
    {
        this._reached = num;
    }
    public int ReachedNumber
    {
        get
        {
            return _reached;
        }
    }
} 

TimeReachedEventArgs has the information about the time interval at which subscriber wants an alert from the publisher. Let’s build a class that contains the business logic for the event that will fire on every time interval elapsed till the meeting starts.

public  event TimeReachedEventHandler TimeReached; 

We have created our event but if you look at the code carefully you will find that our event is of type delegate. Apart from the event, we have the methods AltertMe and OnNumberReached. The AlterMe method accepts the total time for the meeting and the alert wanted after the time period. It starts a counter till it equals to the interval for the alert. Then it passed the time elapsed to TimeReachedEventArgs. Then it passed the TimeReachedEventArgs object to the OnNumberReached method.

public void AltertMe(int timeToMeetingTime, int alertAfterTime)
{
    if (timeToMeetingTime < alertAfterTime)
        throw new ArgumentException("Alter time is less than the meeting time.");
    Stopwatch st = new Stopwatch();
    st.Start();
    while(true)
    {
        int interval=TimeSpan.FromTicks(st.ElapsedTicks).Seconds;
        if (interval == alertAfterTime)
        {
            TimeReachedEventArgs e = new TimeReachedEventArgs(alertAfterTime);
            OnNumberReached(e);
            st.Stop();
            return;//do not count any more;
        }
    }
}  

The OnNumberReached method accepts the TimeReachedEventArgs object and invokes the event TimeReached.

protected virtual void OnNumberReached(TimeReachedEventArgs e)
{
    TimeReached(this, e);
}

We have the following code in the back end of the UI that will listen to the event. On button click we have call the event for each time interval.

public partial class Form1 : Form
{
    Counter _meetingTimer;
    public Form1()
    {
        InitializeComponent();
        _meetingTimer = new Counter();
        _meetingTimer.TimeReached += new TimeReachedEventHandler(AlterMe);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            int timeToMeeting = Convert.ToInt16(txtTimeToMeeting.Text);
            int alertTime = Convert.ToInt16(txtAlertTime.Text);
            while (timeToMeeting > alertTime)
            {
                _meetingTimer.AltertMe(timeToMeeting, alertTime);
                alertTime += alertTime;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private void AlterMe(object s, TimeReachedEventArgs r)
    {
        MessageBox.Show("Alterting you after:- "+r.ReachedNumber);
    }
} 

What is an Asynchronous Delegate?

Sometimes we need to perform a large task and we do not want to wait till the task ends, instead we prefer to move to the next task and when the large task is finished we want a notification that the large task has completed successfully or not. A real life situation and the answer is pretty simple Asynchronous execution of task.

Let’s see what asynchronous execution is with the help of the following diagram.

Consider the above diagram. We have a large task that takes 15 minutes to execute and a small task that takes 2 minutes to execute.

1) We are processing synchronously and we allow the lager task to process first.

Our small task needs to wait till our large task gets finished. 15 minutes is the minimum wait time for task 2. As both the tasks are independent, it is not logical for our small task to wait till the large task finishes its execution.

2) Let’s process our large task asynchronously.

We start processing our large task first but instead of waiting for the large task to be finished we move in parallel to task 2 or subsequent tasks in the pipeline and execute them; when our large task finishes its execution we will take output from it.

Let’s build the above scenario with the help of a Delegate.

public class DelegateDemo4
{
    delegate Guid DoComplexLogic();
    public static void Main(string[] args)
    {
        DoComplexLogic docomplexLogic = MyComplexLogic;
        docomplexLogic.BeginInvoke(new AsyncCallback(CallBackMyComplexTask), docomplexLogic);
        Console.WriteLine("New task is started!");
        Console.Read();
    }
    public static void CallBackMyComplexTask(IAsyncResult asyncResult)
    {
        DoComplexLogic doComplexLogic = (DoComplexLogic)asyncResult.AsyncState;
        Guid newGuid= doComplexLogic.EndInvoke(asyncResult);
        Console.WriteLine(newGuid);
    }
    public static Guid MyComplexLogic()
    {
        Thread.Sleep(3000);
        return Guid.NewGuid();
    }
}

When the developer call the Delegate using the .BeginInvoke() method, the developer is specifying asynchronous execution of the method that is encapsulated by the Delegate. When we use .BeginInvoke(), we need to use a callback mechanism, i.e., when our huge task has finished execution it will call the callback method.

We have captured the async result, i.e., when our huge task has finished execution it will send the result to the callback method. We use the .EndInvoke() method and pass the result of the execution to it to get the return value form the method execution.

We have finished our simple example of Asynchronous Delegates.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here