C# delegates
C# delegates are awesome. What does delegation mean? Delegation, in simple terms, is to hand off a task.
Let’s say John is the CEO and Matthew is a manager who works for John. Daryl and Mary are developers who work for Matthew. CEO John asks Manager Matthew to create for him an desktop application and a web application. Manager Matthew asks Developer Daryl to make a desktop application and Developer Mary to make a web application. In other words, Matthew delegated a task to Daryl and then another task to Mary.
C# delegates do the same thing. Delegates points to a function/method. When the delegate is invoked, it just hands off the effort to the function. There’s a condition. Delegate’s return type and signature (parameters) must match exactly the function it points to. If the function takes string and int as parameters and returns string, delegate must be declared as such also. Let’s take an example.
using System; namespace ConsoleApplication1 { public delegate void CallAnotherFunction(string message); class Program { static void Main() { CallAnotherFunction callFunction; callFunction = Function1; callFunction("Hi Function1"); callFunction = Function2; callFunction("Hi Function2"); Console.Read(); } static void Function1(string message) { Console.WriteLine("Received: " + message); } static void Function2(string message) { Console.WriteLine("My name is Function2 and I just received this message: " + message); } } } Result: Received: Hi Function1 My name is Function2 and I just received this message: Hi Function2
In this example, we declare a delegate CallAnotherFunction. It returns void and takes one string as a parameter. This delegate will point to any function that returns void and has a single string as its signature/parameter.
We made two functions that return void and take a string to see how delegate works. We create a delegate variable callFunction and then point it to Function1 We invoke callFunction and provide it a string. Doing so will call Function1 with the string parameter and print “Hi Function1” on the console.
Then, we point callFunction to Function2 and invoke it with a string. Function2 will be invoked with a string parameter and will print “My name is Function2 and I just received this message: Hi Function2” on the console. If you come from a C++ background, delegates are like function pointers.
Let’s take a longer example.
using System; using System.Collections.Generic; namespace ConsoleApplication2 { // Declare a delegate. It's signature is a boolean // Delegate can point to any function that accepts a boolean public delegate void CarDelegate(bool shouldPerformAction); /// <summary> /// Car class that just declares two methods. WashCar and PerformOilChange /// /// These two methods take a boolean value and based on the received information /// it does something; in our case, just prints out an appropriate message /// /// We will use a single delegate to call both these method /// </summary> class Car { /// <summary> /// Print whether or not we should wash the car /// </summary> /// <param name="wash">True, if car should be washed; false otherwise</param> public void WashCar(bool wash) { if (wash) { Console.WriteLine("Okay, will wash car"); return; } Console.WriteLine("There is no need to wash car"); } /// <summary> /// Print whether or not we should change car's oil /// </summary> /// <param name="oilChange">True, if car's oil should be changed; false otherwise</param> public void PerformOilChange(bool oilChange) { if (oilChange) { Console.WriteLine("Okay, we should perform oil change"); return; } Console.WriteLine("There is no need perform oil change"); } } /// <summary> /// Startup class /// </summary> class Program { /// <summary> /// This method will take our delegate (which points to a function of our choice) /// and passes that delegate (i.e. the function it points to) true or false /// </summary> /// <param name="carWork">Delegate pointing to the work to be done on the car</param> /// <param name="shouldPerform">True, if the task should be performed; false otherwise</param> static void WorkOnCar(CarDelegate carWork, bool shouldPerform) { carWork(shouldPerform); } /// <summary> /// Address each task and inform car whether to work on them or not /// </summary> /// <param name="tasks">Key-value of tasks -and- whether to work on them</param> static void WorkOnCarTasks(Dictionary<CarDelegate, bool> tasks) { foreach (var task in tasks) { // task.key is the delegate - whatever it may point to // if the first task is WashCar of car object and its value is true, // the following statement will call WashCar(true) of car object task.Key(task.Value); } } /// <summary> /// Let's implement delegates /// </summary> static void Main() { var c = new Car(); // create a variable of the delegate type we just declared above CarDelegate del; // METHOD 1 // point the delegate to car's WashCar method // and send it true del = c.WashCar; del(true); // use the same delegate to point to car's PerformOilChange method // and send it true del = c.PerformOilChange; del(false); // METHOD 2 // Call the method WorkOnCar and supply it the method to run and the value with it WorkOnCar(c.WashCar, true); WorkOnCar(c.PerformOilChange, false); // METHOD 3 // create tasks for the car as a key-value pair of Car's method and the value it will receive // send the tasks to a method that will then call it using our delegate var tasksForCar = new Dictionary<CarDelegate, bool> { {c.WashCar, true}, {c.PerformOilChange, false} }; WorkOnCarTasks(tasksForCar); Console.Read(); } } } Results: Okay, will wash car There is no need perform oil change Okay, will wash car There is no need perform oil change Okay, will wash car There is no need perform oil change
In the above example, we declare a delegate CarDelegate. It will point to any function that returns void and takes boolean as a parameter.
To test it, we create a Car class and create two methods matching the return type and the signature like the delegate expects. Our goal is to tell the Car object what tasks to do. Instead of calling car’s methods directly, we use a single delegate variable and ask it to call car’s methods. We use three methods of doing so.
First example uses the single delegate variable to call both methods of Car object. The second example calls a method that accepts a delegate and boolean as parameters. The third example calls a method that accepts key-value (delegate and boolean) and invokes car’s methods.
Hopefully this will give an easy visualization of delegates. These are very simplistic examples of delegates. MSDN provides exhaustive information about delegates.