First of, I have read many explanations on SO and blogs about covariance and contravariance and a big thanks goes out to Eric Lippert for producing such a great series on Co
(Edited in response to comments)
This MSDN article on the topic described covariance and contravariance as it applies to matching a function to a delegate. A variable of the delegate type:
public delegate bool Compare(Giraffe giraffe, Dolphin dolphin);
could (because of contravariance) be populated with the function:
public bool Compare(Mammal mammal1, Mammal mammal2)
{
return String.Compare(mammal1.Name, mammal2.Name) == 0;
}
From my reading, it doesn't have to do with calling the function directly, but matching functions with delegates. I'm not sure that it can be boiled down to the level you demonstrate, with individual variables or object assignments being contravariant or covariant. But the assignment of a delegate uses contravariance or covariance in a way that makes sense to me according to the linked article. Because the delegate's signature contains more derived types than the actual instance, this is referred to as "contravariance", something separate from "covariance" in which a delegate's return type is less derived than the actual instance.