BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

C# Programming
.NET 1.1+

C# Delegates

The fourteenth part of the C# Object-Oriented Programming tutorial explains how to add delegates to classes and programs. Delegates are used to provide references to methods that may be altered at run-time. They are also essential when creating events.

What is a Delegate?

A delegate is a special kind of object that holds a reference to a method. The delegate can be called as if it were any other method. However, when called, the underlying referenced method is executed. This simple layer of abstraction is useful because unlike a direct call, the method being used does not need to be known when writing the code. The reference is created at run-time and may be changed repeatedly throughout the life of the executing program.

NB: If you have previously used C++ function pointers then you will see many similarities between these and delegates. Unlike function pointers, C# delegates are object-oriented, secure and type-safe.

Multicasting

Delegates provide a useful facility known as multicasting. When a delegate is used for multicasting, two or more method references may be added to the delegate object. In this situation, a single call to the delegate function actually calls each of the added methods in turn. The combination of the referenced items is known as the invocation chain or invocation list.

Asynchronous Callbacks

Another interesting use of delegates is known as asynchronous callback. As a delegate is an object, it may be passed as a parameter or assigned as the value of a property. This allows a delegate to be created and passed to a long-running, asynchronous process. When the process has finished executing, the delegate can be called to indicate completion. Asynchronous callbacks are beyond the scope of this article.

Creating a Delegate

Syntax

To create a delegate, the delegate keyword is used as a prefix to the method's signature as follows:

delegate return-type name(parameters);

The return-type is the data type that will be returned from a method that is referenced by the delegate. The name is the name of the delegate and the parameters section contains the comma-separated list of parameters used when calling the delegate. The return-type and parameters form the signature of the delegate and this must match the signature of the method that is linked to it, though the reference method may be static or linked to a particular instance of a class.

Creating a Simple Delegate

To demonstrate the declaration and use of a simple delegate we will create a program as an example. To begin, create a new console application named "DelegateDemo". Add a class file named "Scoreboard" to the project. This class will hold methods that calculate a score for a fictitious game that requires the player to hit targets in the quickest time possible. Versions of the calculation will be available for adults and children and we will use a delegate to determine which is used.

Add the following code to the Scoreboard class to create the two scoring mechanisms:

// 10 points per target, -2 point per second
public static int CalculateAdultScore(int seconds, int targets)
{
    return (targets * 10) - (seconds * 2);
}

// 15 points per target, -1 point per second
public static int CalculateChildScore(int seconds, int targets)
{
    return (targets * 15) - seconds;
}

Declaring the Delegate

We will assign which scoring calculation is used at run-time within the Main method of the program. To be able to change the scoring method at run-time, a delegate must be declared. The delegate can be declared either within the class, in which case it is available only to that class's members, in the namespace so that it is available to all contained classes or outside of the namespace to be globally visible. In this case, add the following code within the application's "Program" class.

delegate int CalculateScore(int seconds, int targets);

NB: In the sample the parameters have the same names as those of the methods that will be referenced by the delegate. This is not a requirement though the data types must match.

Referencing a Method

Finally we need to link a target method to the delegate so it can be called. Add the following code to the Main method:

CalculateScore getScore;
int time = 60;
int targets = 20;

getScore = new CalculateScore(Scoreboard.CalculateAdultScore);
Console.WriteLine("Adult score: {0}", getScore(time, targets));

getScore = new CalculateScore(Scoreboard.CalculateChildScore);
Console.WriteLine("Child score: {0}", getScore(time, targets));

/* OUTPUT

Adult score: 80
Child score: 240

*/

The code in the Main method starts by declaring an object named "getScore" based upon the CalculateScore delegate. This object will be called instead of the target method that is assigned at run-time. The two integers simply represent the data for the time taken and the number of targets hit that will be used to calculate a score.

Next, the getScore object is initialised. This is similar in syntax to instantiating any other object. The key difference is that the parameter that is required is a reference to the method that is to be linked to the delegate object. In this case, because the called method is static, the class and method names are provided. If this were an instance method, you would substitute the class name with the name of an object.

The first Console.WriteLine statement outputs the calculated score using adult scoring rules. It does this by calling the getScore delegate object as if it were the referenced method. The delegate effectively passes control to the CalculateAdultScore method.

Finally, the getScore object is re-instantiated, this time using the children's scoring mechanism. When the method is executed for the second time, the outputted score is therefore different.

Removing the Reference

Once a delegate's method reference is no longer required, it should be removed. This provides more efficient garbage collection and release of resources when required. To remove the reference, simply set the object's value to null. For the scoring example above, the following code is used:

getScore = null;

Creating a Multicasting Delegate

Earlier in the article I mentioned multicasting delegates. These interesting variants on the standard delegate permit multiple methods to be referenced by a single delegate object. When the delegate function is called, each of the referenced methods is executed in turn. In the case of the delegates that we will create in the remainder of this article, the execution order is the same as the order in which the references are added.

To demonstrate the declaration and use of a multicasting delegate we will modify the Scoreboard class and the Main method of the previously created program. We will start by adding two methods to the Scoreboard class, each providing a facility to output a status message to the console.

Add the following code to the Scoreboard class to create the two status message methods:

// Target hit message
public void TargetHit()
{
    Console.WriteLine("Target hit!");
}

// Points awarded message
public void PointsAwarded()
{
    Console.WriteLine("Points awarded!");
}

NB: Note that this time we are using instance rather than static methods so will be calling the methods of an instantiated Scoreboard object.

Declaring the Delegate

Declaring a multicasting delegate is no different to declaring a simple one. To create the new delegate, add the following code within the application's "Program" class.

delegate void ShowStatus();
27 January 2008