Home

Delegates and Events

 

Delegates

 

Introduction

The C and C++ languages have long enjoyed 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. In fact, a delegate is created like an interface but as a method. Based on this, 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 Notepad and, in the empty file, type the following:
     
    using System;
    
    namespace LoanProcessing
    {
    	class Exercise
    	{
    		static double GetPrincipal()
    		{
    			double P;
    
    			Console.Write("Enter the Principal: $");
    			P = double.Parse(Console.ReadLine());
    			return P;
    		}
    
    		static double GetInterestRate()
    		{
    			double r;
    
    			Console.Write("Enter the Interest Rate (%): ");
    			r = double.Parse(Console.ReadLine());
    			return r;
    		}
    
    		static int GetPeriod()
    		{
    			int t;
    
    			Console.Write("Enter the number of months: ");
    			t = int.Parse(Console.ReadLine());
    			return t;
    		}
    		  
    		static int Main()
    		{
    			double Principal, IntRate;
    			int    NumberOfPeriods;
    
    			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("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    			Console.WriteLine("Loan Processing\n");
    			Principal      = GetPrincipal();
    			IntRate        = GetInterestRate();
    			NumberOfPeriods= GetPeriod();
    			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} months", NumberOfPeriods);
    			Console.WriteLine("==================================\n");
    		  
    			return 0;
    		}
    	}
    }
  2. Save the file as Exercise.cs in a new folder called LoanProcess1 created inside your CSharp Lessons folder
  3. Open the Command Prompt and switch to the folder that contains the current exercise
  4. To compile the program, type csc /out:"Loan Evaluation".exe Exercise.cs and press Enter
  5. To execute it, type "Loan Evaluation" and press Enter
    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: $4500
    Enter the Interest Rate (%): 12.55
    Enter the number of months: 24
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    ==================================
    Estimate on loan
    ----------------------------------
    Principal: $4,500.00
    Interest:  12.55 %
    Period:    24 months
    ==================================
  6. Return to Notepad
  7. 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.
    Change the Exercise.cs file as follows:
     
    using System;
    
    namespace LoanProcessing
    {
    	class Exercise
    	{
    		static double GetPrincipal()
    		{
    			double P;
    
    			Console.Write("Enter the Principal: $");
    			P = double.Parse(Console.ReadLine());
    			return P;
    		}
    
    		static double GetInterestRate()
    		{
    			double r;
    
    			Console.Write("Enter the Interest Rate (%): ");
    			r = double.Parse(Console.ReadLine());
    			return r;
    		}
    
    		static 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");
    			}
    		}
    		  
    		static int Main()
    		{
    			double Principal, IntRate;
    			int    TypeOfPeriod = 0;
    			int    Periods = 0;
    			string PeriodName = null;
    
    			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("\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    			Console.WriteLine("Loan Processing\n");
    			Principal      = GetPrincipal();
    			IntRate        = GetInterestRate();
    
    			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("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\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("==================================\n");
    
    			return 0;
    		}
    	}
    }
  8. Save the file
  9. Compile and test it
  10. Select the whole contents of the file (Ctrl+A) and copy it to the clipboard (Ctrl+C)
  11. Start a new instance of Notepad
  12. To make the program more professional, paste the file in it and change it as follows:
     
    using System;
    
    namespace LoanProcessing
    {
    	class Evaluation
    	{
    		public double GetPrincipal()
    		{
    			double P;
    
    			Console.Write("Enter the Principal: $");
    			P = double.Parse(Console.ReadLine());
    			return P;
    		}
    
    		public double GetInterestRate()
    		{
    			double r;
    
    			Console.Write("Enter the Interest Rate (%): ");
    			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;
    		}
    	}
    }
  13. Save the file as Evaluation.cs
  14. Change the Exercise.cs file as follows:
     
    using System;
    
    namespace LoanProcessing
    {
    	class Exercise
    	{
    		static int Main()
    		{
    			Evaluation Borrow = new Evaluation();
    			double Principal, IntRate, Period, AmountPaidAsInterest;
    			int    TypeOfPeriod = 0;
    			double Periods = 0;
    			string PeriodName = null;
    
    			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("\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    			Console.WriteLine("Loan Processing\n");
    			Principal      = Borrow.GetPrincipal();
    			IntRate        = Borrow.GetInterestRate();
    			Period         = Borrow.GetPeriod(ref TypeOfPeriod, ref Periods);
    			AmountPaidAsInterest = Borrow.InterestAmount(Principal, IntRate, Period);
    
    			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("Interest paid on Loan: {0:C}", AmountPaidAsInterest);
    			Console.WriteLine("==================================\n");
    
    			return 0;
    		}
    	}
    }
  15. Save the file
  16. Switch to the Command Prompt
  17. To compile the program, type csc /out:"Loan Evaluation".exe Evaluation.cs Exercise.cs and press Enter
  18. To execute it, type "Loan Evaluation" and press Enter. 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: $850
    Enter the Interest Rate (%): 14.55
    How do you want to enter the length of time?
    1 - In Days
    2 - In Months
    3 - In Years
    Your Choice: 1
    Enter the number of days: 240
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    ==================================
    Estimate on loan
    ----------------------------------
    Principal: $850.00
    Interest:  14.55 %
    Period:    240 days
    --------------------------------
    Interest paid on Loan: $82.45
    ==================================
  19. Return to the Exercise.cs file

Delegate Declaration

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

[attributes] [modifiers] delegate result-type identifier ([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 name for a method.

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 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 assignment the method is supposed to 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 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 that 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. To declare and use a delegate, change the Exercise.cs file as follows:
     
    using System;
    
    namespace LoanProcessing
    {
    	delegate double Add2Values(double Value1, double Value2);
    
    	class Exercise
    	{
    		static int Main()
    		{
    			Evaluation Borrow = new Evaluation();
    			double Principal, IntRate, Period, AmountPaidAsInterest;
    			int    TypeOfPeriod = 0;
    			double Periods = 0;
    			string PeriodName = null;
    
    			Add2Values Add = new Add2Values(Addition);
    
    			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("\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    			Console.WriteLine("Loan Processing\n");
    			Principal      = Borrow.GetPrincipal();
    			IntRate        = Borrow.GetInterestRate();
    			Period         = Borrow.GetPeriod(ref TypeOfPeriod, ref Periods);
    			AmountPaidAsInterest = Borrow.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;
    		}
    	}
    }
  2. Test the program. 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: $850
    Enter the Interest Rate (%): 14.55
    How do you want to enter the length of time?
    1 - In Days
    2 - In Months
    3 - In Years
    Your Choice: 1
    Enter the number of days: 240
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    ==================================
    Estimate on loan
    ----------------------------------
    Principal: $850.00
    Interest:  14.55 %
    Period:    240 days
    --------------------------------
    Total Amount Paid:     $932.45
    Interest paid on Loan: $82.45
    ==================================
  3. 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.

Delegates and Arguments

 

A Delegate That Takes One of More Arguments

If you want to associate a method that takes arguments to a delegate, when declaring the delegate, provide the necessary argument(s) in its parentheses. Here is an example of a delegate that takes two arguments (and returns a value):

delegate double Addition(double x, double y);

When defining the associated method, besides returning the same type of value if not void, make sure that the method takes the same number of arguments. Here is an example:

using System;

delegate void dlgSimple();
delegate double Addition(double x, double y);

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

	private static double Plus(double a, double b)
	{
		return a + b;
	}

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

Once again, to associate the method, declare a variable of the type of delegate and pass the name of the method to the constructor of the delegate. Here is an example:

Addition Add = new Addition(Plus);

Notice that only the name of the method is passed to the delegate. To actually use the delegate, when calling it, in its parentheses, provide a value for the argument(s) conform to the type specified when declaring the delegate. Here is an example:

using System;

delegate void dlgSimple();
delegate double Addition(double x, double y);

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

	private static double Plus(double a, double b)
	{
		return a + b;
	}

	static int Main()
	{
		double Value1 = 248.66, Value2 = 50.26, Result;
		dlgSimple Announce = new dlgSimple(Welcome);
		Addition  Add = new Addition(Plus);

		Result = Add(Value1, Value2);

		Announce();
		Console.WriteLine("\n{0} + {1} = {2}", Value1, Value2, Result);

		return 0;
	}
}

This would produce:

Welcome to the Wonderful World of C# Programming!

248.66 + 50.26 = 298.92

A Delegate Passed as Argument

Using delegates, one method can be indirectly passed as argument to another method. To proceed, first declare the necessary delegate. Here is a example of such a delegate:

using System;

namespace GeometricFormulas
{
	public delegate double Squared(double x);

	public class Circle
	{
		private double _radius;

		public double Radius
		{
			get { return _radius; }

			set  { _radius = value; }
		}
	}
}

A delegate can be passed as argument to a method. Such an argument would be used as if it were a method itself. This means that, when accessed in the body of the method, the name of the delegate must be accompanied by parentheses and if the delegate takes an argument or argument, the argument(s) must be provided in the parentheses of the called delegate. Here is an example:

using System;

namespace GeometricFormulas
{
	public delegate double Squared(double x);

	public class Circle
	{
		private double _radius;

		public double Radius
		{
			get { return _radius; }

			set	{ _radius = value; }
		}

		public double Area(Squared sqd)
		{
			return sqd(_radius) * Math.PI;
		}
	}
}

After declaring a delegate, remember to define a method that implements the needed behavior of that delegate. Here is an example:

using System;

namespace GeometricFormulas
{
	public delegate double Squared(double x);

	public class Circle
	{
		private double _radius;

		public static double ValueTimesValue(double Value)
		{
			return Value * Value;
		}
	}
}

You can also define the associated method in another class, not necessarily in the class where the delegate would be needed. Once the method that implements the delegate is known, you can use the delegate as you see fit. To do that, you can declare a variable of the type of that delegate and pass the implementing method to its constructor. Here is an example:

using System;

namespace GeometricFormulas
{
	public delegate double Squared(double x);

	public class Circle
	{
		private double _radius;


		public static double ValueTimesValue(double Value)
		{
			return Value * Value;
		}

		public double Area(Squared sqd)
		{
			return sqd(_radius) * Math.PI;
		}

		public void CircleCharacteristics()
		{
			Squared Sq = new Squared(ValueTimesValue);
		}
	}

}

This declaration gives life to the delegate and can then be used as we have proceed with delegates so far.

Here is an example:

using System;

namespace GeometricFormulas
{
	public delegate double Twice(double x);
	public delegate double Squared(double x);

	public class Circle
	{
		private double _radius;

		public static double TwoTimes(double Value)
		{
			return Value * 2;
		}

		public static double ValueTimesValue(double Value)
		{
			return Value * Value;
		}

		public double Radius
		{
			get { return _radius; }

			set	{ _radius = value; }
		}

		public Circle()
		{
			_radius = 0;
		}

		public Circle(double r)
		{
			_radius = r;
		}

		public double Diameter(double rad)
		{
			return rad * 2;
		}

		public double Circumference(Twice FDiam)
		{
			double Circf;
		
			Circf = FDiam(_radius);
			return Circf * Math.PI;
		}

		public double Area(Squared sqd)
		{
			return sqd(_radius) * Math.PI;
		}

		public void CircleCharacteristics()
		{
			Squared Sq = new Squared(ValueTimesValue);
			Twice   Tw = new Twice(TwoTimes);

			Console.WriteLine("Circle  Characteristics");
			Console.WriteLine("Radius:        {0}", Radius);
			Console.WriteLine("Diameter:      {0}", Diameter(_radius));
			Console.WriteLine("Circumference: {0}", Circumference(Tw));
			Console.WriteLine("Area:          {0}\n", Area(Sq));
		}
	}

	class Exercise
	{
		static int Main()
		{
			Circle Round = new Circle(25.58);

			Round.CircleCharacteristics();
			return 0;
		}
	}
}

This would produce:

Circle  Characteristics
Radius:        25.58
Diameter:      51.16
Circumference: 160.723880157654
Area:          2055.65842721639

An Array of Delegates

To further refine the call to a group of methods that perform the same kind of task, you can declare an array of delegates. Before creating the array, you must first know or have the methods you would be referring to. These methods must have a similar signature. This means that they must return the same type of value, they must have the same number of arguments and they must  have the  same type(s) of argument(s). Here are examples of such functions:

class Exercise
{
	const double PI = 3.14159;
		
	static double Diameter(double Radius)
	{
		return Radius * 2;
	}
		  
	static double Circumference(double Radius)
	{
		return Diameter(Radius) * PI;
	}
	
	static double Area(double Radius)
	{
		return Radius * Radius * PI;
	}
}

To declare an array of delegates, proceed as you would for a normal C# array as we have done so far. Here is an example:

using System;

class Exercise
{	
	static double Diameter(double Radius)
	{
		return Radius * 2;
	}
		  
	static double Circumference(double Radius)
	{
		return Diameter(Radius) * PI;
	}
	
	static double Area(double Radius)
	{
		return Radius * Radius * PI;
	}
	
	static int Main()
	{
		Measure[] Calc = new Measure[3];

		return 0;
	}
}

You can initialize each member using its index and calling the corresponding method. This can be done as follows:

using System;

delegate double Measure(double R);

class Exercise
{
	const double PI = 3.14159;
		
	static double Diameter(double Radius)
	{
		return Radius * 2;
	}
		  
	static double Circumference(double Radius)
	{
		return Diameter(Radius) * PI;
	}
	
	static double Area(double Radius)
	{
		return Radius * Radius * PI;
	}
	
	static int Main()
	{
		double R = 12.55;
		Measure[] Calc = new Measure[3];

		Calc[0]  = new Measure(Diameter);
		double D = Calc[0](R);
		Calc[1]  = new Measure(Circumference); 
		double C = Calc[1](R);
		Calc[2]  = new Measure(Area);
		double A = Calc[2](R);

		Console.WriteLine("Circle Characteristics");
		Console.WriteLine("Diameter:      {0}", D);
		Console.WriteLine("Circumference: {0}", C);
		Console.WriteLine("Area:          {0}\n", A);

		return 0;
	}
}

This would produce:

Circle Characteristics
Diameter:      25.1
Circumference: 78.8539
Area:          494.808

Practical Learning Practical Learning: Using an Array of Delegates

  1. Start a new file in Notepad and type the following:
     
    using System;
    
    namespace MultipleChoiceQuestion
    {
    	class Exercise
    	{
    		enum TMCQuestion { One, Two, Three, Four, Five };
    		delegate char Question();
    
    		static char Sequence()
    		{
    			char Answer;
    
    			Console.WriteLine("Which sequence of numbers does not appear ");
    			Console.WriteLine("to follow a recognizable order?");
    			Console.WriteLine("(a)   3   9  27  33");
    			Console.WriteLine("(b)   3   6   9  12");
    			Console.WriteLine("(c)   2   4   6   8");
    			Console.WriteLine("(d) 102 204 408 816");
    			Console.Write("Your Answer? ");
    			Answer = char.Parse(Console.ReadLine());
                
    			return Answer;
    		}
    
    		static char Expression()
    		{
    			char Response;
    
    			Console.WriteLine("Select the best expression to complete the empty space");
    			Console.WriteLine("When ... drugs to a business address, traffickers often ");
    			Console.WriteLine("omit a recipient name");
    			Console.WriteLine("(a) to send");
    			Console.WriteLine("(b) senders");
    			Console.WriteLine("(c) sending");
    			Console.WriteLine("(d) dealing");
    			Console.Write("Your Answer? ");
    			Response = char.Parse(Console.ReadLine());
                
    			return Response;
    		}
    
    		static char Sentence()
    		{
    			char Answer;
    
    			Console.WriteLine("Even ... there are 76,000 lawyers in that city, ");
    			Console.WriteLine("it is still a small community");
    			Console.WriteLine("(a) although");
    			Console.WriteLine("(b) though");
    			Console.WriteLine("(c) for");
    			Console.WriteLine("(d) since");
    			Console.Write("Your Answer? ");
    			Answer = char.Parse(Console.ReadLine());
                
    			return Answer;
    		}
    
    		static char  WrongWord()
    		{
    			char Wrong;
    
    			Console.WriteLine("Select the wrong word that would complete ");
    			Console.WriteLine("the sentence");
    			Console.WriteLine("For this type of business, revenue gains are ...");
    			Console.WriteLine("(a) limited");
    			Console.WriteLine("(b) scarce");
    			Console.WriteLine("(c) limitless");
    			Console.WriteLine("(d) claiming");
    			Console.Write("Your Answer? ");
    			Wrong = char.Parse(Console.ReadLine());
                
    			return Wrong;
    		}
    
    		static char Right()
    		{
    			char Sentence;
    
    			Console.WriteLine("Select the right sentence");
    			Console.WriteLine("(a) The company is expecting to reducing inventory,");
    			Console.WriteLine("    control cost, and efficiency improvement.");
    			Console.WriteLine("(b) The company expects to reduce inventory,");
    			Console.WriteLine("    control cost, and improve efficiency.");
    			Console.WriteLine("(c) The company expects to reduce inventory,");
    			Console.WriteLine("    control cost, and improving efficiency.");
    			Console.WriteLine("(d) The company is expecting to reducing inventory,");
    			Console.WriteLine("    controlling cost, and efficiency improvement.");
    			Console.Write("Your Answer? ");
    			Sentence = char.Parse(Console.ReadLine());
                
    			return Sentence;
    		}
    
    		static int Main()
    		{
    			const int NumberOfQuestions = 5;
    			char[] Answer = new char[NumberOfQuestions];
    
    			Question[] MCQ = new Question[NumberOfQuestions];
    		
    			MCQ[0] = new Question(Sequence);
    			MCQ[1] = new Question(Expression);
    			MCQ[2] = new Question(Sentence);
    			MCQ[3] = new Question(WrongWord);
    			MCQ[4] = new Question(Right);
    
    			for(int i = 0; i < NumberOfQuestions; i++)
    			{
    				Console.WriteLine("Question {0}", i + 1);
    				Answer[i] = MCQ[i]();
    				ValidateAnswer(i+1, Answer[i]);
    			}
    
    			return 0;
    		}
    
    		static void ValidateAnswer(int QstNbr, char Ans)
    		{
    			switch(QstNbr)
    			{
    				case 1:
    					if(Ans == 'a' || Ans == 'A')
    						Console.WriteLine("Right Answer");
    					else
    					{
    						Console.WriteLine("Wrong Answer - The right answer was 'a'");
    						Console.WriteLine("(a) Starting at 3, 3*3=9 and 3*9=27");
    						Console.WriteLine("    There is no obvious way to determine 33");
    					}
    					break;
    
    				case 2:
    					if(Ans == 'c' || Ans == 'C')
    						Console.WriteLine("Right answer");
    					else
    						Console.WriteLine("Wrong Answer - The right answer was 'c'");
    					break;
        
    				case 3:
    					if(Ans == 'b' || Ans == 'B')
    						Console.WriteLine("Right answer");
    					else
    						Console.WriteLine("Wrong Answer - The right answer was 'b'");
    					break;
                        
    				case 4:
    					if(Ans == 'd' || Ans == 'D')
    						Console.WriteLine("Right answer");
    					else
    						Console.WriteLine("Wrong Answer - The right answer was 'd'");
    					break;
    				
    				case 5:
    					if(Ans == 'b' || Ans == 'B')
    						Console.WriteLine("Right answer");
    					else
    						Console.WriteLine("Wrong Answer - The right answer was 'b'");
    					break;
    
    				default:
    					Console.WriteLine("Invalid Answer");
    					break;
    			}
    		}
    	}
    }
  2. Save the file in a new folder named MCQ1
  3. Save the file itself as Exercise.cs
  4. At the Command Prompt, compile and execute the file

Events

 

Introduction

Except for the main class of your program (the class that contains the Main() method), every class is mostly meant to interact with other, either to request values and methods of the other classes or to provide other classes with some values or a behavior they need. When a class A requests a value or service from another class B, class A is referred to as a client of class B. This relationship is important not simply because it establishes a relationship between both classes but also because class B should be ready to provide the value or behavior that a client needs at a certain time.

While a class B is asked to provide some values or methods to another class A, many things would happen. In fact, there is an order that things should follow. For example, during the lifetime of a program, that is, while a program is running, a class may be holding a value it can provide to its client but at another time, that value may not be available anymore, for any reason; nothing strange, this is just the ways it happens. Because different things can happen to a class B while a program is running, and because only class B would be aware of these, it must be able to signal to the other classes when there is a change. This is the basis of events: An event is an action that occurs on an object and affects it in a way that its clients must be made aware of. Events are mostly familiar to those who do graphical (GUI) programming as they are able to "visually" work on Windows controls and as they are able to access the objects on which actions are happening and the objects that must know when these actions occur. Still, because events are dealt with in C#, you should be aware of their functionality.

Author Note Events were never part of traditional computer languages such as C/C++ or Pascal. These languages were developed without the operating system in mind. In fact, they were made platform independent so they can be used on any operating system regardless of how that OS functions. Event were highly used at the operating system level. Therefore, they were implemented in operating system libraries like Win32. On the other hands, because C# was developed a long time after its parent the C++, and because it was meant to substitute Win32 knowledge, events were built in the languages. Still, all languages of the .NET Framework are "aware" of the concept of events.
 

Although events are mostly used in Windows controls programming, they can also be implemented in console applications.

 

Event Creation

An event is declared like a pseudo-variable but based on a delegate. Therefore, to declare an event, you must have a delegate that would implement it. Here is an example:

using System;

delegate void dlgSimple();

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

To actually declare an event, you use the event keyword with the following formula:

[attributes] [modifiers] event type declarator;
[attributes] [modifiers] event type member-name {accessor-declarations};

The attributes factor can be a normal C# attribute.

The modifier can be one or a combination of the following keywords: public, private, protected, internal, abstract, new, override, static, virtual, or extern.

The event keyword is required. It is followed by the name of the delegate that specifies its behavior. If the event is declared in the main class, it should be made static. Like everything in a program, an event must have a name. This would allow the clients to know what (particular) event occurred. Here is an example:

using System;

delegate void dlgSimple();

class Exercise
{
	public static event dlgSimple Simply;

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

After declaring the event, you must define a method that calls the event. Here is an example:

using System;

delegate void dlgSimple();

class Exercise
{
	public static event dlgSimple Simply;

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

	public static void SayHello()
	{
		Simply();
	}
}

When the event occurs, its delegate would be invoked. This specification is also referred to as hooking up an event. As the event occurs (or fires), the method that implements the delegate runs. This provides complete functionality for the event and makes the event ready to be used. Before using an event, you must combine it to the method that implements it. This can be done by passing the name of the method to the appropriate delegate, as we learned when studying delegates. You can then assign this variable to the event's name using the += operator. Once this is done, you can call the event. Here is an example:

using System;

delegate void dlgSimple();

class Exercise
{
	public static event dlgSimple Simply;

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

	public static void SayHello()
	{
		Simply();
	}

	static int Main()
	{
		Simply += new dlgSimple(Welcome);

		SayHello();

		return 0;
	}
}

Instead of the += operator used when initializing the event, you can implement add and remove of the event class. Here is an example:

using System;

delegate void dlgSimple();

class Exercise
{
	public event dlgSimple Simply
	{
		add
		{
			Simply += new dlgSimple(Welcome);
		}
		remove
		{
			Simply -= new dlgSimple(Welcome);
		}
	}

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

Previous Copyright © 2004-2012, FunctionX Next