What is a delegate?
A delegate is a proxy, an alternative, or a representative of someone else. For example, we may read in the newspaper that a delegate from another country is coming to our country to meet a high official. This person is a delegate because they have come to our country to represent their own country. They could be a representative for the president, prime minister, or any other high official of that country. Let’s imagine that the delegate is representing the president of a country. Maybe the president was unable to attend this meeting in person for some reason, and that is why a delegate was sent on their behalf. This delegate will do the same work that the president was supposed to do on the trip and make decisions on behalf of the president. The delegate is not a fixed individual; could be any qualified person that the president chooses.
The concept of a delegate is similar in software development. We can have a functionality where a method doesn’t do the actual work that it was ask to do, but rather, it will call another method to execute that work. Furthermore, in programming, the method that doesn’t do the actual work, but passes it to another method, is called a delegate. Consequently, a delegate will actually hold a reference of a method. When the delegate is called, the referenced method will actually be called and executed. Now, you may ask, “Why should I call a delegate if it is going to call another method? Why don’t I just call the method directly?”. Well, we do this because if you directly call the method, you lose your flexibility by making your code coupled. You are hard coding the method name in your code so that, whenever that line of code will run, that method will be executed. However, with a delegate, you can decide which method to call at runtime instead of compile time.
At he very top, we can see the declaration of the delegate, the delegate keyword indicate to the compiler that we are declaring a delegate. Then we set the return type to int and named the delegate MathFunc, we also passed two int parameters in this delegate. After that we have two methods in addition to the main method, one is Add and the other is Sub, if you pay close attention to these methods, you will see that they have the same signature as the delegate, this is done deliberately, because a method can use a delegate when the method has the same signature as the delegate.
Method Group Conversion
In the previous example, we saw how can create an object of a delegate and pass the method name in the constructor, now we will look a another way of achieving the same thing. With method group conversion, you don’t need to initialize the delegate object, but you can directly assign the method to it.
We can see that instead of passing the method name in the constructor, we directly assign the method to it.
Using the static and instance methods as delegates
In the previous examples, we used static methods in the delegates, however you can also use instance methods in delegates.
To use those methods in delegates, we first have to create an object of that class and simply assign the methods to a delegate using the object instance.
With multicasting, you can assign more than one method to a delegate. When that delegate is executed, it runs all the methods that were assigned one after another. Using the + or += operator, you can add methods to a delegate. There is also a way to remove added methods from the delegate. To do this, you have to use the – or -= operator.
Here, we can see how our delegate executed the two methods one after the other. We have to keep in mind that it works like a queue, so the first method you add will be the first method to get executed.
Covariance and Contravariance
In the previous examples the return type and the parameters of the method and the delegate have to be the same. However, with the use of the concepts of covariance and contravariance, you can actually register methods to a delegate that don’t have the same return types or parameters. The delegate will then be able to execute them when called. Covariance is when you assign a method to a delegate that has a return type that is a derived type of the delegate’s return type. For example, if class B is derived from class A, and if the delegate returns class A, then a method can be registered to the delegate that returns class B.
On the other hand, contravariance is when a method is passed to a delegate and the parameters of the method don’t match the parameters of the delegate. Here, we have to keep in mind that the parameter type of the method has to be at least derived from the parameter type of the delegate.
You can think of an event as a kind of method that gets executed in some circumstances and notifies handlers or delegates about that incident. You can also create different event handlers and assign those event handlers to an event so that, whenever that event gets fired, it will notify all the registered handlers that will perform their work.
You can multicast events in the same way that you can in a delegates. This means that you can register multiple event handlers (methods that have subscribed to the event) to an event and all of them will be executed one by one when the event gets fired. To multicast, you have to use the += sign to register event handlers to the event. You can also remove event handlers from the event by using the -= operator. When you apply multicast, the first event handler that was registered will get executed first, then the second, and so on. By multicasting, you can easily extend or reduce event handlers in your application without doing much work.