Home

Delegates and Events

 

Delegates

 

Introduction

The C and C++ concept of function pointer was very useful when programming for the Microsoft Windows operating systems because the Win32 library relies on the concept of callback functions 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 like a class. In fact, a delegate is created like an interface but appears 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, either no argument or one or more arguments.

Practical LearningPractical Learning: Introducing Delegates

  1. Start Notepad and type the following:
     
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace Calculations
    {
    	public class Exercise : System.Windows.Forms.Form
    	{
    		private System.Windows.Forms.Label lblSide;
    		private System.Windows.Forms.TextBox txtSide;
    		private System.Windows.Forms.Label lblArea;
    		private System.Windows.Forms.TextBox txtArea;
    		private System.Windows.Forms.TextBox txtVolume;
    		private System.Windows.Forms.Label lblVolume;
    		
    		public Exercise()
    		{
    			InitializeComponent();
    		}
    		
    		private void InitializeComponent()
    		{
    			this.lblSide = new System.Windows.Forms.Label();
    			this.txtSide = new System.Windows.Forms.TextBox();
    			this.txtArea = new System.Windows.Forms.TextBox();
    			this.lblArea = new System.Windows.Forms.Label();
    			this.txtVolume = new System.Windows.Forms.TextBox();
    			this.lblVolume = new System.Windows.Forms.Label();
    			
    			// Label: Side
    			this.lblSide.Location = new System.Drawing.Point(16, 19); 
    			this.lblSide.Size = new System.Drawing.Size(64, 14);
    			this.lblSide.TabIndex = 0;
    			this.lblSide.Text = "Side:";
    
    			// TextBox: Side
    			this.txtSide.Location = new System.Drawing.Point(88, 16);
    			this.txtSide.TabIndex = 1;
    			this.txtSide.Text = "0.00";
    			
    			// Label: Area
    			this.lblArea.Location = new System.Drawing.Point(16, 50);
    			this.lblArea.Size = new System.Drawing.Size(64, 14);
    			this.lblArea.TabIndex = 2;
    			this.lblArea.Text = "Area:";
    
    			// TextBox: Area
    			this.txtArea.Location = new System.Drawing.Point(88, 48);
    			this.txtArea.TabIndex = 3;
    			this.txtArea.Text = "0.00";
    
    			// Label: Volume
    			this.lblVolume.Location = new System.Drawing.Point(16, 83);
    			this.lblVolume.Size = new System.Drawing.Size(64, 14);
    			this.lblVolume.TabIndex = 4;
    			this.lblVolume.Text = "Volume:";
    			
    			// TextBox: Volume 
    			this.txtVolume.Location = new System.Drawing.Point(88, 80);
    			this.txtVolume.TabIndex = 5;
    			this.txtVolume.Text = "0.00";
    			
    			// Form: Exercise
    			this.Size = new System.Drawing.Size(216, 152);
    			this.Controls.Add(this.lblVolume);
    			this.Controls.Add(this.txtVolume);
    			this.Controls.Add(this.lblArea);
    			this.Controls.Add(this.txtArea);
    			this.Controls.Add(this.lblSide);
    			this.Controls.Add(this.txtSide);
    			this.MaximizeBox = false;
    		this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
    			this.Text = "Cube Calculation";
    		}
    
    		static int Main() 
    		{
    			Application.Run(new Exercise());
    
    			return 0;
    		}
    	}
    }
  2. Save it as Exercise.cs in a new folder named Calculations
  3. Open the Command Prompt and switch to the Calculations folder
  4. To compile the exercise, type csc Exercise.cs and press Enter
  5. To execute the application, type Exercise and press Enter

Declaring a Delegate

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.

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 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.

After implementing the method, 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. The declaration gives meaning to the delegate. To actually use the method, call the name of the delegate as if it were a defined method.

Here is an example that uses a delegate:

using System;
using System.Windows.Forms;

delegate void dlgSimple();

public class Exercise : System.Windows.Forms.Form
{
	public Exercise()
	{
		dlgSimple Announce = new dlgSimple(Welcome);

		Announce();

		InitializeComponent();
	}

	private static void Welcome()
	{
	
	}
	
	private void InitializeComponent()
	{
	
	}

	static void Main() 
	{
		Application.Run(new Exercise());
	}
}

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;
using System.Windows.Forms;

delegate double Addition();

public class Exercise : System.Windows.Forms.Form
{
	private TextBox txtBox;

	public Exercise()
	{
		Addition  Add = new Addition(Plus);

		InitializeComponent();
		
		txtBox.Text = Add().ToString();
	}

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

		return a + b;
	}
	
	private void InitializeComponent()
	{
		txtBox = new TextBox();
		txtBox.Location = new Point(100, 20);

		Controls.Add(txtBox);
	
	}

	static int Main() 
	{
		Application.Run(new Exercise());

		return 0;
	}
}

This would produce:

 

Delegates and Classes

In the above introductions, we associated delegates with only methods of the main class. Because of the way delegates are declared, 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.

 

Practical LearningPractical Learning: Using Delegates in Classes

  1. To declare a delegate, change the file as follows:
     
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace Calculations
    {
    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 class Exercise : System.Windows.Forms.Form
    {
    	private System.Windows.Forms.Label lblSide;
    	private System.Windows.Forms.TextBox txtSide;
    	private System.Windows.Forms.Label lblArea;
    	private System.Windows.Forms.TextBox txtArea;
    	private System.Windows.Forms.TextBox txtVolume;
    	private System.Windows.Forms.Label lblVolume;
    	private Cube SmallBox;
    
    	public Exercise()
    	{
    		SmallBox = new Cube(25.58);
    
    		Multiplication AreaDefinition = new Multiplication(SmallBox.Area);
    		Multiplication VolDefinition    = new Multiplication(SmallBox.Volume);
    
    		InitializeComponent();
    
    		txtSide.Text   = SmallBox.Side.ToString();
    		txtArea.Text   = AreaDefinition().ToString();
    		txtVolume.Text = VolDefinition().ToString();
    	}
    		
    	private void InitializeComponent()
    	{
    		. . . No Change
    	}
    
    	static int Main() 
    	{
    		Application.Run(new Exercise());
    
    		return 0;
    	}
    }
    }
  2. Save it and switch to the Command Prompt
  3. Compile it by typing csc Exercise.cs and pressing Enter
  4. Execute it by typing Exercise and press Enter
     
  5. Close the form and return to Notepad

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.

 

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 double Addition(double x, double y);

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

	static int Main()
	{
		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.

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 arguments, 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.

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 others, 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 an object B is asked to provide some values to, or perform some assignment(s) for, another object A, many things would happen. In fact, there is an order that the actions should follow. For example, during the lifetime of a program, that is, while a program is running, an object 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 way it happens. Because different things can happen to an object B while a program is running, and because only object 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. 

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. 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()
	{
	
	}
}

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()
	{
	
	}

	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()
	{
	
	}
}
 
 

Previous Copyright 2004-2005 FunctionX, Inc. Next