Delegates are one of the most used features of C#. It allows you to pass a function as of function pointer. It is kind of same as function pointer of C++.
Put simply, delegates are the same as a function pointer of C ++. It refers to another function.
As noted in Microsoft official documentation:
“A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.”
Before get deeper into technical jargon about delegates, let us create first a delegate.
// declaring a delegate public delegate int AddDelegate(int num1, int num2); // 1 static void Main(string[] args) { AddDelegate d1 = new AddDelegate(add); // 2 int result = d1(7, 2); // 3 Console.WriteLine(result); Console.ReadKey(true); } public static int add(int a, int b) // function { Console.WriteLine(" A Delegate Called me"); int res; res = a + b; return res; }
Let us talk through the above code.
- Just before main function in comment #1, we are declaring a delegate named AddDelegate. The signature of a delegate is very important, because a delegate can only refer functions matching the same signature.
- We created delegate with return type set to integer and it takes two input integer parameters.
- In comment # 2, we are instantiating delegate and passing add function as reference.
- In comment # 3 invoking the delegate.
On running above, you should get output as shown in the below image:
One important thing you need to keep in mind is that the signature of delegates must match with the signature of the function pointed by the delegate. Not only signature, but also return type should match.
Now, I hope you understand how a delegate can be created and used. Some features of delegates are as follows:
- Delegates are similar to C++ function pointers:
- But it is type safe
- Can encapsulate object instance
- Can encapsulate method
- Delegate allows methods to pass as a parameter.
- Delegates can be chained together.
- Delegates can be used to define a callback method. For example, you can use it inside an event.
- Delegates can compile anonymous method and lambda expressions.
You can encapsulate a lambda expression in a delegate as shown in next code listing:
AddDelegate d2 = (a, b) => { Console.WriteLine(" delegate lambda expression "); int res1 = a + b + 10; return res1; }; int result2 = d2(10, 20); Console.WriteLine(result2);
You will get expected output as shown in below image:
You can encapsulate more than one function in a delegate using multicast delegate. You use multicast delegates to assign more than one object to one instance of an object. In other words, you can have more than one subscriber for the same delegate instance, and this is very useful in assigning multiple subscribers to an event. Consider this next code listing to understand an example of multicast delegates.
class Program { public delegate void AddDelegate(int num1, int num2); static void Main(string[] args) { AddDelegate d1 = add; d1 += add100; d1 += (int a, int b) => { int res = (a + b) * 1000; Console.WriteLine(res); }; d1(10, 20); Console.ReadKey(true); } public static void add(int a, int b) { int res; res = a + b; Console.WriteLine(res); } public static void add100(int a, int b) { int res; res = a + b + 100; Console.WriteLine(res); } } }
We have created a delegate then added multiple functions to encapsulate in the delegate instance reference. We are adding functions add, add100, and a lambda expression to the same delegate instance reference.
Now when we call the delegate all these three references will be called, and you will get expected output as shown in the below image. You can use + or – to add or remove an object reference from a multicast delegate.
Delegates have two more important concepts
- Covariance
- Contravariance
From the beginning of this article, we have been saying that a delegate can refer a method with the same return type and signature, however, variance ease this requirement. Covariance and Contravariance provide flexibility for matching a delegate type with a method signature.
Covariance permits a method to have a return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.
To understand covariance, let us create two classes as shown in next code listing:
public class Animal { protected int age; protected void displayAge(int age) { this.age = age; Console.WriteLine(this.age); } } public class Dog : Animal { public string color; public void displayColor() { displayAge(99); Console.WriteLine(this.color); } } }
Dog class inherits Animal class. So using covariance, a delegate with type Animal can refer Dog type. We are creating delegate with return type Animal and assigning reference of Dog type in below code listing. This is possible due to covariance feature of delegate.
public delegate Animal AnimalDelegate(); static void Main(string[] args) { AnimalDelegate an = newDog; an(); Console.ReadKey(true); } static Dog newDog() { Dog d = new Dog(); d.color = "white"; d.displayColor(); return d; }
Like variance, contravariance allows us to work with methods that have parameters of a type that are base types of the delegate signature parameter type. Now I hope you understand delegates in C# and you can use this function in your projects whenever required. Besides the topics covered above, you may also want to check generic delegates.
If you like this article , feel free to check Infragistics Ultimate Product suite here