I'm having a struggle understanding these two concepts.
Yes you are. Many people do.
But I think after many videos and SO QA's, I have it distilled down to its simplest form:
You have not.
Covariance means that a sub-type can do what its base-type does.
No. That's the Liskov Substitution Principle.
Contravariance means you can treat a sub-type the same way you would treat its base-type.
No. That's just re-stating what you said for covariance.
The real distillation of covariance and contravariance is:
A covariant conversion preserves the direction of another conversion.
A contravariant conversion reverses the direction of another conversion.
Dog
is convertible to Animal
. IEnumerable<Dog>
is convertible to IEnumerable<Animal>
.
The direction is preserved, so IEnumerable<T>
is covariant. IComparable<Animal>
is convertible to IComparable<Dog>
, which reverses the direction of the conversion, so it is contravariant.
I understand mathematically what covariance means, and so I guess it's the same in compsci.
Just to be clear: mathematicians use "variance" to mean a bunch of different things. The meaning that is common to mathematics and computer science is the category theory definition.
In C# it's just a matter of where and in what ways these two types of relationships are supported?
Mathematically, variance tells you about whether a relation is preserved or reversed by a mapping. If we have the mapping T --> IEnumerable<T>
and the relation "is convertible to via identity or reference conversion" then it is the case that in C#, if X relates to Y then IE<X>
relates to IE<Y>
. The mapping is therefore said to be covariant with respect to the relation.
what is it that these features are trying to accomplish by supporting them?
People frequently requested "I have a method that takes a sequence of animals and I have a sequence of turtles in hand; why do I have to copy the sequence to a new sequence to use the method?" That's a reasonable request, we got it frequently, and we got it a lot more frequently after LINQ made it easier to work with sequences. It's a generally useful feature that we could implement at a reasonable cost, so we implemented it.