English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Covariance and contravariance make us more flexible when dealing with class hierarchies.
Before learning covariance and contravariance, please look at the following class hierarchy:
public class Small { } public class Big: Small { } public class Bigger : Big { }
According to the example class above, Small is the base class of Big, and Big is the base class of Bigger. One thing to remember here is that derived classes will always have more than the base class, so the base class is smaller than the derived class.
Now, let's see the following initialization:
As shown above, the base class can accommodate derived classes, but derived classes cannot accommodate the base class. In other words, an instance can accept large even if it is required to be small, but cannot accept small if it is required to be large.
Now, let's understand covariance and contravariance.
Covariance allows you to pass derived types where a base type is required. Covariance is like the variance of the same type. The base class and other derived classes are considered to be the same type of classes that add additional functions to the base type. Therefore, covariance allows you to use derived classes where base classes are needed (if a subclass is required, the rule: it can accept a superclass).
Covariance can be applied to delegates, generics, arrays, interfaces, and more.
Covariance in delegates allows for flexibility in the return type of delegate methods.
public delegate Small covarDel(Big mc); public class Program { public static Big Method1(Big bg) { Console.WriteLine("Method1); return new Big(); } public static Small Method2(Big bg) { Console.WriteLine("Method2); return new Small(); } public static void Main(string[] args) { covarDel del = Method1; Small sm1 = del(new Big()); del = Method2; Small sm2 = del(new Big()); } }
Method1 Method2
As you can see in the example above, the delegate expects the return type to be Small (base class), but we can still assign a Method that returns Big (derived class)1and Method with the same signature as expected by the delegate2.
Therefore, covariance allows you to assign methods to delegates with fewer derived return types.
Apply Contravariance (Contravariane) to the parameters. Contravariance (Contravariane) allows base class parameters to be assigned to methods that expect derived class parameters for the delegate.
Continuing with the above example, add a Method with a different parameter type than the delegate3:
delegate Small covarDel(Big mc); class Program { static Big Method1(Big bg) { Console.WriteLine("Method1); return new Big(); } static Small Method2(Big bg) { Console.WriteLine("Method2); return new Small(); } static Small Method3(Small sml) { Console.WriteLine("Method3); return new Small(); } static void Main(string[] args) { covarDel del = Method1; del += Method2; del += Method3; Small sm = del(new Big()); }
Method1 Method2 Method3
As you can see, Method3With a parameter of type Small, while the delegate expects a parameter of type Big. However, you can assign Method3Used with delegates.
You can also use covariance and contravariance in the same way as shown below.
delegate Small covarDel(Big mc); class Program { static Big Method4(Small sml) { Console.WriteLine("Method3); return new Big(); } static void Main(string[] args) { covarDel del = Method4; Small sm = del(new Big()); } }
Method4