Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple examples of co and contravariance

Could someone provide me simple C# examples of convariance, contravariance, invariance and contra-invariance (if such thing exists).

All samples I've seen so far was just casting some object into System.Object.

like image 365
Grant Smith Avatar asked Jan 12 '11 14:01

Grant Smith


People also ask

What is difference between covariance and 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.

What is covariance and contravariance in generics?

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.

What is covariant C#?

Covariance in C# Covariance enables you to pass a derived type where a base type is expected. Co-variance is like variance of the same kind. The base class and other derived classes are considered to be the same kind of class that adds extra functionalities to the base type.

What is covariance and contravariance in generics in Java?

Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them.


2 Answers

Could someone provide me simple C# examples of convariance, contravariance, invariance and contra-invariance (if such thing exists).

I have no idea what "contra-invariance" means. The rest are easy.

Here's an example of covariance:

void FeedTheAnimals(IEnumerable<Animal> animals)  {      foreach(Animal animal in animals)         animal.Feed(); } ... List<Giraffe> giraffes = ...; FeedTheAnimals(giraffes); 

The IEnumerable<T> interface is covariant. The fact that Giraffe is convertible to Animal implies that IEnumerable<Giraffe> is convertible to IEnumerable<Animal>. Since List<Giraffe> implements IEnumerable<Giraffe> this code succeeds in C# 4; it would have failed in C# 3 because covariance on IEnumerable<T> did not work in C# 3.

This should make sense. A sequence of Giraffes can be treated as a sequence of Animals.

Here's an example of contravariance:

void DoSomethingToAFrog(Action<Frog> action, Frog frog) {     action(frog); } ... Action<Animal> feed = animal=>{animal.Feed();} DoSomethingToAFrog(feed, new Frog()); 

The Action<T> delegate is contravariant. The fact that Frog is convertible to Animal implies that Action<Animal> is convertible to Action<Frog>. Notice how this relationship is the opposite direction of the covariant one; that's why it is "contra" variant. Because of the convertibility, this code succeeds; it would have failed in C# 3.

This should make sense. The action can take any Animal; we need an action that can take any Frog, and an action that can take any Animal surely can also take any Frog.

An example of invariance:

void ReadAndWrite(IList<Mammal> mammals) {     Mammal mammal = mammals[0];     mammals[0] = new Tiger(); } 

Can we pass an IList<Giraffe> to this thing? No, because someone is going to write a Tiger into it, and a Tiger cannot be in a list of Giraffes. Can we pass an IList<Animal> into this thing? No, because we are going to read a Mammal out of it, and a list of Animals might contain a Frog. IList<T> is invariant. It can only be used as what it actually is.

For some additional thoughts on the design of this feature, see my series of articles on how we designed and built it.

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

like image 147
Eric Lippert Avatar answered Oct 01 '22 11:10

Eric Lippert


Invariance(in this context) is the absence of both co- and contra-variance. So the word contra-invariance doesn't make sense. Any type parameter that's not tagged as either in or out is invariant. This means this type parameter can both be consumed and returned.

A good example of co-variance is IEnumerable<out T> because an IEnumerable<Derived> can be substituted for an IEnumerable<Base>. Or Func<out T> which returns values of type T.
For example an IEnumerable<Dog> can be converted to IEnumerable<Animal> because any Dog is an animal.

For contra-variance you can use any consuming interface or delegate. IComparer<in T> or Action<in T> come to my mind. These never return a variable of type T, only receive it. Wherever you expect to receive a Base you can pass in a Derived.

Thinking of them as input-only or output-only type-parameters makes it easier to understand IMO.

And the word invariants is typically not used together with type variance, but in the context of class or method invariants, and represents a conserved property. See this stackoverflow thread where the differences between invariants and invariance are discussed.

like image 20
CodesInChaos Avatar answered Oct 01 '22 11:10

CodesInChaos