A delegate is a form of a type-safe function pointer. More simply put, it is an object that knows how to call a method, i.e. a reference to a method. Delegates are useful as they assist us in writing flexible and extendable applications.
Delegates are useful when using the eventing design pattern also known as the observer pattern. The eventing or observer pattern consists of an object, called the subject or publisher, which maintains a list of dependent objects, called observers or subscribers, and notifies them automatically of a state change in the subject-object by raising an event and then calling a method on the observer objects by using a delegate. We will cover this pattern in more detail when I cover events in a separate post.
For now, let us simply focus on delegates.
So, let us look at an example:
public class KillerRobot { public string Name { get; set; } } public class KillerRobotActuator { public delegate void KillerRobotActionHandler(KillerRobot robot); //Declare delegate which will act as a //signature for methods that can be //referenced. public void DoAction(string robotName, KillerRobotActionHandler actionHandler) { var robot = new KillerRobot {Name = robotName }; actionHandler(robot); } } public class RobotActions { public void RobotTalk(KillerRobot robot) { Console.WriteLine(“{0} is Talking.”, robot.Name); } public void RobotDance(KillerRobot robot) { Console.WriteLine(“{0} is Dancing.”, robot.Name); } } class Program { static void Main(string[] args) { var robotActuator = new KillerRobotActuator(); var robotActions = new RobotActions(); KillerRobotActuator.KillerRobotActionHandler actionHandler = robotActions.RobotTalk; actionHandler += robotActions.RobotDance; actionHandler += RobotPowerOff; robotActuator.DoAction(“The Geek”,actionHandler); } static void RobotPowerOff(KillerRobot robot) { Console.WriteLine(“{0} has turned off.”, robot.Name); } }
So, in this basic example we have a class KillerRobotActuator which declares a delegate:
public delegate void KillerRobotActionHandler(KillerRobot robot);
Any method that complies to this signature can then be added to this delegate and in the DoAction method where the following is executed:
actionHandler(robot);
All the methods that have been added to the actionHandler will then be executed.
We can see in the Main method of the Program class that a new KillerRobotActionHandler is declared and three method references are added to it as below:
KillerRobotActuator.KillerRobotActionHandler actionHandler = robotActions.RobotTalk; actionHandler += robotActions.RobotDance; actionHandler += RobotPowerOff;
And then finally the DoAction method on KillerRobotActuator is executed, passing in the above declared actionHandler containing references to the three methods:
robotActuator.DoAction(“The Geek”,actionHandler);
All three the methods that are referenced by the actionHandler comply to the signature defined in the delegate declaration in the KillerRobotActuator class. i.e. a void return type and an input parameter of type KillerRobot.
It is also worth mentioning that of the methods referenced, two are contained in a separate class RobotActions and the third is a static method declared in the Program class, so methods from multiple different class locations can be added as long as they comply to the signature of the delegate declares.