About
In this code snippet, we’ll take a look at covariance and contravariance in C#.
Covariance and contravariance enable implicit type conversion for arrays, delegates and generic interface type arguments.
Covariance allows for a more derived type to be used where a less derived type is expected. This can be done implicitly because a more derived type contains all the members of a less derived type.
Contravariance allows for a less derived type to be used where a more derived type is expected. This can be done because a less derived type will have all the members the more derived type has.
Let’s have a look at the code below to see an example of covariance and contravariance.
Code:
namespace CovarianceContravariance { internal class Program { static void Main(string[] args) { //To quote https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/ //"In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. //Covariance preserves assignment compatibility and contravariance reverses it." //Covariance is when a more derived type is assigned to a less derived one. Action<string> funcMyMethodString = MyMethod; Action<string> funcMyMethodObject = funcMyMethodString; //Contravariance is when a less derived type is assigned to a more derived one. Action<object> funcMyOtherMethod = MyOtherMethod; Action<string> funcMyOtherMethodString = funcMyOtherMethod; } static void MyOtherMethod(object input) { //Do something... } static void MyMethod(string input) { //Do something... } //Generic type parameters in interfaces can be covariant(out keyword) or contravariant(in keyword). //Covariant interface methods can have a more derived return type than defined by the generic type parameters. //Contravariant interface methods can have less derived input parameter types than those defined by the generic parameters. interface MyCovariantInterface<out T> { //Methods can return type of T. T MyMethod(); //Methods can't take type of T as an input parameter. //void MyOtherMethod(T input); //Throws error if uncommented. } interface MYContravariantInterface<in T> { //Methods can take type of T as an input parameter. void MyMethod(T input); void DoSomething<U>() where U : T; //Type constraints can be specified if desired. I briefly covered constraints in this post: https://eecs.blog/c-generics/ //Methods can't return type of T. //T MyOtherMethod(); //Throws error if uncommented. } } }