There are two important delegate features. What we have learned so far is that normally, to register a method in a delegate, the method has to match the signature of the delegate. This means that 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. Let's look at the example in the following...