This may be the first time you've read about this new .NET type. A delegate is an object that refers to a static method or an instance method. In this article I discuss what delegates are, how you can create and use them, and how the C# compiler saves us time by generating the delegate's class. We will also look at the MSIL code, talk about multicast delegates and provide callback methods through the use of delegates.
Contributed by Michael Youssef Rating: / 100 February 07, 2007
A delegate is an object that is created to refer to a static method or an instance method, and then used to call this method. To start off, you create a new delegate type in a different way than you create any other class. You use the delegate keyword as in the following statement.
public delegate int DelegateToMethod(int x, int y);
It seems unusual I know, but I will explain how it's done. Let's take a look at the very first example that explains how to use a delegate.
The First Delegate Example
Copy the following code into your VS.NET class file and run the project.
using System;
namespace Delegates { public delegate int DelegateToMethod(int x, int y);
public class Math { public static int Add(int first, int second) { return first + second; }
public static int Multiply(int first, int second) { return first * second; }
public static int Divide(int first, int second) { return first / second; } }
public class DelegateApp { public static void Main() { DelegateToMethod aDelegate = new DelegateToMethod(Math.Add); DelegateToMethod mDelegate = new DelegateToMethod(Math.Multiply); DelegateToMethod dDelegate = new DelegateToMethod(Math.Divide); Console.WriteLine("Calling the method Math.Add() through the aDelegate object"); Console.WriteLine(aDelegate(5,5)); Console.WriteLine("Calling the method Math.Multiply() through the mDelegate object"); Console.WriteLine(mDelegate(5,5)); Console.WriteLine("Calling the method Math.Divide() through the dDelegate object"); Console.WriteLine(dDelegate(5,5)); Console.ReadLine(); }
} }
When you run the above code you will get the following:
Let's explain what's going on in this example step-by-step. We have defined a new delegate type using the statement
public delegate int DelegateToMethod(int x, int y);
You are used to defining a new class using the class keyword, then an identifier followed by {, then implementation in the form of methods, properties and fields followed by }. The case is different with delegates. When we define a new delegate type (like DelegateToMethod) the C# compiler generates a class called DelegateToMethod that derives the System.MultipcastDelegate as follows:
public sealed class DelegateToMethod : System.MulticastDelegate { public DelegateToMethod(object target, int method); public virtual void Invoke(int x, int y); public virtual IAsyncResult BeginInvoke(int x, int y, AsyncCallback callback, object obj); public virtual void EndInvoke(IAsyncResult result); }
The Constructor method of this class takes two arguments. The first is an object reference of the type that defined the instance method that the delegate refers to, and the second is an int value of the function pointer to the method that the delegate encapsulates.
The Invoke() method has the same signature as our delegate declaration. This method is used to call the delegate's encapsulated method. Note that when we defined the delegate we provided a signature for the method that can be encapsulated. In other words, the delegate can't refer to a method with a different signature than the one that it is created with. The BeginInvoke() and EndInvoke() provide asynchronous calls, which are beyond the scope of this article.
The C# compiler generates the sealed class with the four virtual methods, but it doesn't generate any implementation for those methods because they have to be implemented by the Common Language Runtime. So up to the point we have discussed, the folks at Microsoft saved us a lot of time by providing the delegate keyword which we can use to generate a class based on the System.MulticastDelegate. Let's continue our example.
The Math class contains three simple methods (Add, Multiply and Divide) that accept two int values and return an int value. Note that those are static methods. The DelegateApp class creates three DelegateToMethod objects as shown next:
DelegateToMethod aDelegate = new DelegateToMethod(Math.Add); DelegateToMethod mDelegate = new DelegateToMethod(Math.Multiply); DelegateToMethod dDelegate = new DelegateToMethod(Math.Divide);
Those three statements create three delegate objects. I think that the issue that would confuse someone is illustrated in the following screen shot:
As you can see, the signature of the Constructor method is not shown; instead, the signature of the method that can be encapsulated by the delegate is shown, or we can say the signature of the delegate. Any method that accepts two int values can return an int. We can look at a delegate as a type-safe function pointer which means that the parameter list and the return type are known.
We create a delegate object using the new operator and pass it the method to be encapsulated. Note that we have said that the generated class' constructor is passed two parameters, and because the passed methods are static, the first parameter will be null value (if it was an instance method it would be the object reference that defined the method instead of the null value).
We have said that in order to call the delegate's encapsulated method we need to call the delegate's Invoke() method. Actually, we can't call this method directly; instead we use the object reference and pass it the arguments as we did in the above code. Take a look again:
Console.WriteLine(aDelegate(5,5));
The statement calls the encapsulated method (which is Math.Add) and passes the parameters as arguments to the method, which returns the value 10.
Put simply, to use a delegate:
Define a new type that inherits from System.MulticastDelegate class and provide the signature of the methods that can be encapsulated by the new type.
public delegate int DelegateToMethod(int x, int y);
Create an instance or static method that has the same signature as defined by the new delegate type.
public static int Add(int first, int second) { return first + second; }
Create a new delegate object using the new operator and pass the method as a parameter to it.
DelegateToMethod aDelegate = new DelegateToMethod(Math.Add);
Invoke the delegate object and pass the arguments to it which in turn calls the referenced method.
Let's look at our example from the ILDASM tool. Load the tool (type ildasm in the command prompt after you set the Path Environment) then open the Delegates.exe and expand the DelegateToMethod delegate type.
As you can see, the DelegateToMethod class is sealed, extends System.MulticastDelegate, and contains contains the methods .ctor (the Constructor), Invoke(), BeginInvoke() and EndInvoke(). If you click on any of those methods like Invoke() you will find no implementation, because as we said it's runtime implemented, as shown in the next screen shot:
The truly interesting code is in the Main method, so let's take a look at its MSIL Code:
the IL opcode ldnull loads a null value on the stack because, as we have said before, the encapsulated method is a static method and a null value is passed to the constructor of the delegate. The next is the ldftn opcode which pushes an unmanaged pointer to the method Math.Add. the newobj allocates memory for the object (the delegate object) then calls the .ctor (the Constructor). The delegate is calling its referenced method through the following instruction
As you can see, the method Invoke(int32, int32) is called which in turn calls the Math.Add() method. Let's modify our example to work with instance methods.
Copy the following code and paste it in your VS.NET class file
using System;
namespace Delegates { public class Math { // note that the delegate now is a nested type of the Math class public delegate void DelegateToMethod(int x, int y);
public void Add(int first, int second) { Console.WriteLine("The method Add() returns {0}", first + second); }
public void Multiply(int first, int second) { Console.WriteLine("The method Multiply() returns {0}", first * second); }
public void Divide(int first, int second) { Console.WriteLine("The method Divide() returns {0}", first / second); } }
public class DelegateApp { public static void Main() { Math math = new Math(); Math.DelegateToMethod aDelegate = new Math.DelegateToMethod(math.Add); Math.DelegateToMethod mDelegate = new Math.DelegateToMethod(math.Multiply); Math.DelegateToMethod dDelegate = new Math.DelegateToMethod(math.Divide); aDelegate(5,5); mDelegate(5,5); dDelegate(5,5); Console.ReadLine(); } } }
I have made some changes to the example so let's go through them. First I have defined the delegate type as a nest type to the Math class. This is perfectly valid but you have to qualify the name of the delegate with the class name, so we write Math.DelegateToMethod.
I have changed the signature of the delegate so it returns void instead of int. I had to change the signature of the methods of the Math class to return void. I also removed the static keyword.
In the Main method of the class DelegateApp I created an object of the Math class, then I created the three delegate objects, but this time I passed an instance method (not a static method). I called the delegate and passed 5, 5 as the parameters.
I think by now you understand the mechanism of how delegates work, but you don't understand why it's useful or how we can use them efficiently. Just wait until the next article (C# Events) and you will appreciate the use of delegates. Now let's discuss multicast delegates.
As you saw in the MSIL code, the delegate type that we created automatically inherits the System.MulticastDelegate, which provides a functionality that creates a chain of delegates through a linked list. It's better to explain with an example, so let's modify our example to use a multicast delegate.
using System;
namespace Delegates { public class Math { // note that the delegate now is a nested type of the Math class public delegate void DelegateToMethod(int x, int y);
public void Add(int first, int second) { Console.WriteLine("The method Add() returns {0}", first + second); }
public void Multiply(int first, int second) { Console.WriteLine("The method Multiply() returns {0}", first * second); }
public void Divide(int first, int second) { Console.WriteLine("The method Divide() returns {0}", first / second); } }
public class DelegateApp { public static void Main() { Math math = new Math(); Math.DelegateToMethod multiDelegate = null; multiDelegate = new Math.DelegateToMethod(math.Add); multiDelegate += new Math.DelegateToMethod(math.Multiply); multiDelegate += new Math.DelegateToMethod(math.Divide); multiDelegate(10,10); Console.ReadLine(); } } }
A multicast delegate is an object that maintains a linked list of delegates. Invoking the delegate invokes each delegate (which in turn calls its encapsulated method) in the same order that it has been added to the linked list. In our example we have created an object (a delegate) called multiDelegate of type Math.DelegateToMethod and assigned a null value to this object.
The next statement assigns (through the assignment = operator as you already know) a new delegate object that encapsulates the method math.Add(). Using the operator += we assign more delegate objects (thus creating a multicast delegate) to the delegate multiDelegate. Then invoking the delegate invokes all the delegates maintained in its linked list, which in turn calls the encapsulated methods.
The statement multiDelegate(10,10); is similar to our previous example in which we have created three delegate objects and then calling each of them separately. You can remove a delegate object from the linked list using the operator -=. Actually, behind the scenes the method Combine() is used to perform the creation of a multicast delegate. In fact this method returns a new delegate instance. If you load the application with ILDASM you will see the use of this method.
Callback methods illustrate the usefulness of delegates. The callback mechanism may seem unusual at first, but it provides a great functionality which illustrates the delegation of a task to another object. To explain what a callback method is and how we can use it, I'm introducing the following example:
using System;
namespace MyCompany { public class Employee { private string firstName; private string lastName; private decimal salary;
public Employee(string first, string last, decimal salary) { this.firstName = first; this.lastName = last; this.salary = salary; }
public string FirstName { get {return firstName;} set {firstName = value;} } public string LastName { get {return lastName;} set {lastName = value;} } public decimal Salary { get {return salary;} set {salary = value;} }
public override string ToString() { return String.Format("{0} {1} with a payroll of {2}", firstName,lastName, salary); } }
public class Department { private Employee[] emps; private string name; public Department(Employee[] theEmps, string name) { emps = theEmps; this.name = name; } public string Name { get { return name; } }
// declaring a nested delegate type that accepts an Employee instance public delegate void EmployeeCallback(Employee emp);
// This method accepts an EmployeeCallback instance thus // providing the Callback mechanism public void ProcessEmployees(EmployeeCallback callback) { foreach(Employee emp in emps) { callback(emp); } } }
public class Sys { private static void UpdatePayroll(Employee emp) { emp.Salary *= 1.2m; Console.WriteLine("The Employee {0} {1}'s salary increased to {2}", emp.FirstName,emp.LastName,emp.Salary); }
public static void Main() { Employee emp1 = new Employee("Marina", "Joe", 7000m); Employee emp2 = new Employee("Mina", "Nader", 7000m); Employee emp3 = new Employee("Johny", "Hany", 9000m);
foreach(Employee emp in emps) { Console.WriteLine(emp.ToString()); }
Console.WriteLine("nCreating the delegate object"); // creating the delegate instance Department.EmployeeCallback updateCallback = new Department.EmployeeCallback(UpdatePayroll);
Console.WriteLine("calling the method ProcessEmployees()n"); dep.ProcessEmployees(updateCallback);
Console.ReadLine(); } } }
The above namespace contains three classes: Employee, Department and Sys. Before I explain anything I have to say that "The Department class DELEGATES the task of updating the Employees' salaries to the Sys class through the use of a Callback method". Let me explain how it's done.
The Employee class is a simple class with three private fields (firstName, lastName and salary), a Constructor to initialize those fields, and three properties to access the private fields. It overrides the ToString() method. The Department class contains a private array of type Employee and another private field of type string to store the name of the department.
The Constructor initializes those fields and also a public property for the name field. The fun begins at the declaration of the delegate EmployeeCallback, which encapsulates any method that accepts one argument of type Employee. The fun continues with the method ProcessEmployees(EmployeeCallback callback). As you can see this method accepts an EmployeeCallback delegate.
The implementation of this method is very simple. Its foreach statement invokes the passed delegate on each Employee instance stored in the private array. The Sys class has a static method called UpdatePayroll which accepts an Employee instance and then update its salary and prints the current salary to the console.
The Main() method creates three Employee instances then assigns those instances to the emps array. The next statement creates an object of type Department and passes the emps array as an argument to the Constructor in order to initialize the internal Employee array of the Dep object. The foreach statements iterate through the Employee instances, calling their ToString() method which prints the firstName, lastName and the salary of the employee.
We then create a new instance of the type EmployeeCallback called updateCallback, which encapsulates the private static method UpdatePayroll. The last statement calls the method ProcessEmployees() and passes the delegate instance as an argument to the method. The call to the method ProceeEmployees() does the trick of the Callback. If you step into the code of this method you will notice that this method calls the UpdatePayroll() method (although it's a private method of the class Sys) for each Employee instance (through the delegate) of the emps array of the dep object.
So callback methods introduce a way to make a conversation between objects. The Sys class calls the ProcessEmployees() method which in turn calls the private method Sys.UpdatePayroll() through the delegate for each Employee instance to update his salary. So we say that the dep object is delegating the functionality of updating the employees' salaries to the class Sys through a callback method.
To provide the callback mechanism you need to create a delegate and a method that accepts the delegate as a parameter as we did in our example. There are other situations where we can use callback methods like asynchronous calls.