Home

Delegates

 

Introduction

The C and C++ languages have long used the concept of function pointer. This was even more useful when programming for the Microsoft Windows operating systems because the Win32 library relies on the concept of callback functions. Callback functions are used in Microsoft Windows programming to process messages. For this reason and because of their functionality, callback functions were carried out in the .NET Framework but they were defined with the name of delegate.

A delegate is a special type of user-defined variable that is declared globally, like a class. A delegate provides a template for a method, like an interface provides a template for a class. Like an interface, a delegate is not defined. Its role is to show what a useful method would look like. To support this concept, a delegate can provide all the necessary information that would be used on a method. This includes a return type, no argument or one or more arguments.

 

Practical LearningPractical Learning: Introducing Delegates

  1. Start Microsoft Visual C# and create a Console Application named WattsALoan1
  2. To create a new class, on the main menu, click Project -> Add Class...
  3. Change the name of the file to LoanEvaluation and click Add
  4. Change the contents of the file as follows:
     
    using System;
    
    namespace WattsALoan1
    {
        class LoanEvaluation
        {
            public double GetPrincipal()
            {
                Console.Write("Enter the Principal:         $");
                double P = double.Parse(Console.ReadLine());
    
                return P;
            }
    
            public double GetInterestRate()
            {
                Console.Write("Enter the Interest Rate (%): ");
                double r = double.Parse(Console.ReadLine());
    
                return r;
            }
    
            public int GetPeriod()
            {
                Console.Write("Enter the number of months:  ");
                int t = int.Parse(Console.ReadLine());
    
                return t;
            }
        }
    }
  5. Access the Program.cs file and change it as follows:
     
    using System;
    
    namespace WattsALoan1
    {
        class Program
        {
            static int Main()
            {
                int NumberOfPeriods;
                double Principal, IntRate;
                LoanEvaluation loan = new LoanEvaluation();
    
                Console.WriteLine("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
                Console.WriteLine("Loan Processing\n");
                Console.WriteLine("This program allows you to calculate the amount of money a ");
                Console.WriteLine("customer will owe at the end of the lifetime of a loan\n");
    
                Principal = loan.GetPrincipal();
                IntRate = loan.GetInterestRate();
                NumberOfPeriods = loan.GetPeriod();
                Console.WriteLine("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
    
                Console.WriteLine("================================");
                Console.WriteLine("Loan Estimation");
                Console.WriteLine("--------------------------------");
                Console.WriteLine("Principal: {0:C}", Principal);
                Console.WriteLine("Interest:  {0:P}", IntRate / 100);
                Console.WriteLine("Period:    {0} months", NumberOfPeriods);
                Console.WriteLine("================================\n");
    
                return 0;
            }
        }
    }
  6. Execute the application test it.
    Here is an example:
     
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    Loan Processing
    
    This program allows you to calculate the amount of money a
    customer will owe at the end of the lifetime of a loan
    
    Enter the Principal:         $14500
    Enter the Interest Rate (%): 12.25
    Enter the number of months:  48
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    ================================
    Loan Estimation
    --------------------------------
    Principal: $14,500.00
    Interest:  12.25 %
    Period:    48 months
    ================================
    
    Press any key to continue . . .
  7. Close the DOS window
  8. In the above program, the clerk was asked to provide the number of months for the period of the loan. Depending on the loan, one customer may want to specify the number of days necessary to pay the loan. Another customer may want to pay a loan over a number of years. To make this possible, we will allow the clerk to select the type of period for a loan.
    Access the LoanProcecssing.cs file and change the file as follows:
     
    using System;
    
    namespace WattsALoan1
    {
        class LoanEvaluation
        {
            public double GetPrincipal()
            {
                Console.Write("Enter the Principal:         $");
                double P = double.Parse(Console.ReadLine());
    
                return P;
            }
    
            public double GetInterestRate()
            {
                Console.Write("Enter the Interest Rate (%): ");
                double r = double.Parse(Console.ReadLine());
    
                return r;
            }
    
            public void GetPeriod(ref int TypeOfPeriod, ref int Periods)
            {
                Console.WriteLine("How do you want to enter the length of time?");
                Console.WriteLine("1 - In Days");
                Console.WriteLine("2 - In Months");
                Console.WriteLine("3 - In Years");
                Console.Write("Your Choice: ");
                TypeOfPeriod = int.Parse(Console.ReadLine());
    
                if (TypeOfPeriod == 1)
                {
                    Console.Write("Enter the number of days: ");
                    Periods = int.Parse(Console.ReadLine());
                }
                else if (TypeOfPeriod == 2)
                {
                    Console.Write("Enter the number of months: ");
                    Periods = int.Parse(Console.ReadLine());
                }
                else if (TypeOfPeriod == 3)
                {
                    Console.Write("Enter the number of years: ");
                    Periods = int.Parse(Console.ReadLine());
                }
                else
                {
                    TypeOfPeriod = 0;
                    // The user made an invalid selection. So, we will give up
                    Console.WriteLine("Bad Selection\n");
                }
            }
        }
    }
  9. Access the Program.cs file and change it as follows:
     
    using System;
    
    namespace WattsALoan1
    {
        class Program
        {
            static int Main()
            {
                int Periods = 0;
                int TypeOfPeriod = 0;
                double Principal, IntRate;
                string PeriodName = null;
                LoanEvaluation loan = new LoanEvaluation();
    
                Console.WriteLine("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
                Console.WriteLine("This program allows you to calculate the amount of money a ");
                Console.WriteLine("customer will owe at the end of the lifetime of a loan\n");
    
                Console.WriteLine("Loan Processing\n");
                Principal = loan.GetPrincipal();
                IntRate = loan.GetInterestRate();
    
                loan.GetPeriod(ref TypeOfPeriod, ref Periods);
                if (TypeOfPeriod == 0)
                {
                    // Since the user made a bad selection, stop the program here
                    return 0;
                }   // Since this "if" condition has a "return 0" line, if the "if"
                // condition produces true, the "return 0" means the function
                // would be terminated. If the condition is false, the inside of
                // this "if" condition would not execute and the function would
                // continue. This means that, if the condition is false, then
                // the "else' is implied. Therefore, we don't have to write an
                // "else" condition: it is automatic.
    
                if (TypeOfPeriod == 1)
                {
                    PeriodName = "days";
                }
                else if (TypeOfPeriod == 2)
                {
                    PeriodName = "months";
                }
                else if (TypeOfPeriod == 3)
                {
                    PeriodName = "years";
                }
    
                Console.WriteLine("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    
                Console.WriteLine("==================================");
                Console.WriteLine("Loan Estimation");
                Console.WriteLine("----------------------------------");
                Console.WriteLine("Principal: {0:C}", Principal);
                Console.WriteLine("Interest:  {0:P}", IntRate / 100);
                Console.WriteLine("Period:    {0} {1}", Periods, PeriodName);
                Console.WriteLine("==================================\n");
    
                return 0;
            }
        }
    }
  10. Execute the application and test it. Here is an example:
     
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    This program allows you to calculate the amount of money a
    customer will owe at the end of the lifetime of a loan
    
    Loan Processing
    
    Enter the Principal:         $7500.00
    Enter the Interest Rate (%): 8.75
    How do you want to enter the length of time?
    1 - In Days
    2 - In Months
    3 - In Years
    Your Choice: 3
    Enter the number of years: 4
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    ==================================
    Loan Estimation
    ----------------------------------
    Principal: $7,500.00
    Interest:  8.75 %
    Period:    4 years
    ==================================
    
    Press any key to continue . . .
  11. Close the DOS window

Creating a Delegate

To create a delegate, you use the delegate keyword. The basic formula used to create a delegate is:

[attributes] [modifiers] delegate ReturnType Name ([formal-parameters]);

The attributes factor can be a normal C# attribute.

The modifier can be one or an appropriate combination of the following keywords: new, public, private, protected, or internal.

The delegate keyword is required.

The ReturnType can be any of the data types we have used so far. It can also be a type void or the name of a class.

The Name must be a valid C# name.

Because a delegate is some type of a template for a method, you must use parentheses, required for every method. If this method will not take any argument, you can leave the parentheses empty.

Here is an example:

using System;

delegate void dlgSimple();

class Exercise
{
    static int Main()
    {
	return 0;
    }
}

After declaring a delegate, remember that it only provides a template for a method, not an actual method. In order to use it, you must define a method that would carry an assignmentto perform. That method must have the same return type and the same (number of) argument(s), if any. For example, the above declared delegate is of  type void and it does not take any argument. you can define a corresponding method as follows:

using System;

delegate void dlgSimple();

class Exercise
{
    private static void Welcome()
    {
	Console.WriteLine("Welcome to the Wonderful World of C# Programming!");
    }

    static int Main()
    {
	return 0;
    }
}

With such a method implemented, you can associate it to the name of the delegate. To do that, where you want to use the method, first declare a variable of the type of the delegate using the new operator. In the parentheses of the constructor, pass the name of the method. Here is an example

using System;

delegate void dlgSimple();

class Exercise
{
	private static void Welcome()
	{
		Console.WriteLine("Welcome to the Wonderful World of C# Programming!");
	}

	static int Main()
	{
		dlgSimple Announce = new dlgSimple(Welcome);

		return 0;
	}
}

This declaration gives meaning to the declared delegate. To actually use the method, call the name of the delegate as if it were a defined method. Here is an example:

using System;

delegate void dlgSimple();

class Exercise
{
	private static void Welcome()
	{
		Console.WriteLine("Welcome to the Wonderful World of C# Programming!");
	}

	static int Main()
	{
		dlgSimple Announce = new dlgSimple(Welcome);

		Announce();
		return 0;
	}
}

This would produce:

Welcome to the Wonderful World of C# Programming!

You can also declare a delegate that returns a value. When defining a method that would be associated with the delegate, remember that the method must return the same type of value. Here is an example:

 
using System;

delegate void dlgSimple();
delegate double Addition();

class Exercise
{
	private static void Welcome()
	{
		Console.WriteLine("Welcome to the Wonderful World of C# Programming!");
	}

	private static double Plus()
	{
		double a = 248.66, b = 50.28;

		return a + b;
	}

	static int Main()
	{
		dlgSimple Announce = new dlgSimple(Welcome);
		Addition  Add = new Addition(Plus);

		Announce();
		Console.WriteLine("\n248.66 + 50.26 = {0}", Add());
		return 0;
	}
}

This would produce:

Welcome to the Wonderful World of C# Programming!

248.66 + 50.26 = 298.94

Practical Learning Practical Learning: Using a Delegate

  1. Access the LoanEvaluation.cs file and change it as follows:
     
    using System;
    
    namespace WattsALoan1
    {
        class LoanEvaluation
        {
            public double GetPrincipal()
            {
                Console.Write("Enter the Principal:         $");
                double P = double.Parse(Console.ReadLine());
    
                return P;
            }
    
            public double GetInterestRate()
            {
                Console.Write("Enter the Interest Rate (%): ");
                double r = double.Parse(Console.ReadLine());
    
                return r;
            }
    
            public double GetPeriod(ref int TypeOfPeriod, ref double Periods)
            {
                Console.WriteLine("How do you want to enter the length of time?");
                Console.WriteLine("1 - In Days");
                Console.WriteLine("2 - In Months");
                Console.WriteLine("3 - In Years");
                Console.Write("Your Choice: ");
                TypeOfPeriod = int.Parse(Console.ReadLine());
    
                if (TypeOfPeriod == 1)
                {
                    Console.Write("Enter the number of days: ");
                    Periods = double.Parse(Console.ReadLine());
                    return Periods / 360;
                }
                else if (TypeOfPeriod == 2)
                {
                    Console.Write("Enter the number of months: ");
                    Periods = double.Parse(Console.ReadLine());
                    return Periods / 12;
                }
                else if (TypeOfPeriod == 3)
                {
                    Console.Write("Enter the number of years: ");
                    Periods = double.Parse(Console.ReadLine());
                    return Periods;
                }
                else
                {
                    TypeOfPeriod = 0;
                    // The user made an invalid selection. So, we will give up
                    Console.WriteLine("Bad Selection\n");
                    return 0.00;
                }
            }
    
            // Interest = Principal * rate * time in years
            public double InterestAmount(double P, double r, double t)
            {
                return P * (r / 100) * t;
            }
        }
    }
  2. Access the Program.cs file
  3. To declare and use a delegate, change the Program.cs file as follows:
     
    using System;
    
    namespace WattsALoan1
    {
        delegate double Add2Values(double Value1, double Value2);
    
        class Exercise
        {
            static int Main()
            {
                double Principal, IntRate, Period, AmountPaidAsInterest;
                int TypeOfPeriod = 0;
                double Periods = 0D;
                string PeriodName = null;
                LoanEvaluation loan = new LoanEvaluation();
    
                Add2Values Add = new Add2Values(Addition);
    
                Console.WriteLine("\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
                Console.WriteLine("This program allows you to calculate the amount of money a ");
                Console.WriteLine("customer will owe at the end of the lifetime of a loan\n");
    
                Console.WriteLine("Loan Processing\n");
                Principal = loan.GetPrincipal();
                IntRate   = loan.GetInterestRate();
                Period    = loan.GetPeriod(ref TypeOfPeriod, ref Periods);
                AmountPaidAsInterest = loan.InterestAmount(Principal, IntRate, Period);
                double Amount = Add(Principal, AmountPaidAsInterest);
    
                if (TypeOfPeriod == 0)
                {
                    // Since the user made a bad selection, stop the program here
                    return 0;
                }   // Since this "if" condition has a "return 0" line, if the "if"
                // condition produces true, the "return 0" means the function
                // would be terminated. If the condition is false, the inside of
                // this "if" condition would not execute and the function would
                // continue. This means that, if the condition is false, then
                // the "else' is implied. Therefore, we don't have to write an
                // "else" condition: it is automatic.
    
                if (TypeOfPeriod == 1)
                {
                    PeriodName = "days";
                }
                else if (TypeOfPeriod == 2)
                {
                    PeriodName = "months";
                }
                else if (TypeOfPeriod == 3)
                {
                    PeriodName = "years";
                }
    
                Console.WriteLine("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n");
    
                Console.WriteLine("==================================");
                Console.WriteLine("Estimate on loan");
                Console.WriteLine("----------------------------------");
                Console.WriteLine("Principal: {0:C}", Principal);
                Console.WriteLine("Interest:  {0:P}", IntRate / 100);
                Console.WriteLine("Period:    {0} {1}", Periods, PeriodName);
                Console.WriteLine("--------------------------------");
                Console.WriteLine("Total Amount Paid:     {0:C}", Amount);
                Console.WriteLine("Interest paid on Loan: {0:C}", AmountPaidAsInterest);
                Console.WriteLine("==================================\n");
    
                return 0;
            }
    
            static double Addition(double Value1, double Value2)
            {
                return Value1 + Value2;
            }
        }
    }
  4. Execute the program and test it. Here is an example:
     
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    This program allows you to calculate the amount of money a
    customer will owe at the end of the lifetime of a loan
    
    Loan Processing
    
    Enter the Principal:         $12500
    Enter the Interest Rate (%): 10.55
    How do you want to enter the length of time?
    1 - In Days
    2 - In Months
    3 - In Years
    Your Choice: 2
    Enter the number of months: 42
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    ==================================
    Estimate on loan
    ----------------------------------
    Principal: $12,500.00
    Interest:  10.55 %
    Period:    42 months
    --------------------------------
    Total Amount Paid:     $17,115.63
    Interest paid on Loan: $4,615.63
    ==================================
    
    Press any key to continue . . .
  5. Return to the Exercise.cs file

Delegates and Classes

In the above introductions, we associated delegates with only method of the main class. Because delegates are usually declared globally, that is outside of a class, they can be associated with a method of any class, provided the method has the same return type (and the same (number of) argument(s)) as the delegate. When we created the methods of the main class, we defined them as static, since all methods of the main class must be declared static.

Methods of any class can also be associated to delegates. Here is an example of two methods associated with a common delegate:

using System;

namespace CSharpLessons
{
	delegate double Multiplication();

	public class Cube
	{
		private double _side;

		public double Side
		{
			get { return _side; }

			set	{ _side = value; }
		}

		public Cube()
		{
			_side = 0;
		}

		public Cube(double s)
		{
			_side = s;
		}

		public double Area()
		{
			return 6 * Side * Side;
		}

		public double Volume()
		{
			return Side * Side * Side;
		}

		public void CubeCharacteristics()
		{
			Multiplication AreaDefinition = new Multiplication(Area);
			Multiplication VolDefinition  = new Multiplication(Volume);

			Console.WriteLine("Cube  Characteristics");
			Console.WriteLine("Side:   {0}", Side);
			Console.WriteLine("Area:   {0}", AreaDefinition());
			Console.WriteLine("Volume: {0}\n", VolDefinition());
		}
	}

	class Exercise
	{
		static int Main()
		{
			Cube SmallBox = new Cube(25.58);

			SmallBox.CubeCharacteristics();
			return 0;
		}
	}
}

This would produce:

Cube  Characteristics
Side:   25.58
Area:   3926.0184
Volume: 16737.925112

Delegates Compositions

One of the characteristics that set delegates apart from C/C++ function pointers is that one delegate can be added to another using the + operation. This is referred to as composition. This is done by adding one delegate variable to another as in a = b + c.


Home Copyright © 2006-2007 FunctionX, Inc. Next