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 Covariance and Contravariance.
However I have a more specific question that I am trying to get my head around a little bit.
As far as I understand per Eric's explanation is that Covariance and Contravariance are both adjectives that describe a transformation. Covariant transformation is that which preserves the order of types and Contravariant transformation is one that reverses it.
I understand covariance in such a manner that I think most developers understand intuitively.
//covariant operation
Animal someAnimal = new Giraffe();
//assume returns Mammal, also covariant operation
someAnimal = Mammal.GetSomeMammal();
The return operation here is covariant as we are preserving the size in which both Animal is still bigger than Mammal or Giraffe. On that note most return operations are covariant, contravariant operations would not make sense.
//if return operations were contravariant
//the following would be illegal
//as Mammal would need to be stored in something
//equal to or less derived than Mammal
//which would mean that Animal is now less than or equal than Mammal
//therefore reversing the relationship
Animal someAnimal = Mammal.GetSomeMammal();
This piece of code of course would not make sense to most developers.
My confusion lies in Contravariant argument parameters. If you had a method such as
bool Compare(Mammal mammal1, Mammal mammal2);
I have always learned that input parameters always force contravariant behavior. Such that if the type is used as an input parameter its behavior should be contravariant.
However what is the difference between the following code
Mammal mammal1 = new Giraffe(); //covariant
Mammal mammal2 = new Dolphin(); //covariant
Compare(mammal1, mammal2); //covariant or contravariant?
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant?
By the same token that you can't do something like this you can't do
//not valid
Mammal mammal1 = new Animal();
//not valid
Compare(new Animal(), new Dolphin());
I guess what I am asking is, what makes method argument passing a contravariant transformation.
Sorry for the long post, maybe I am understand this incorrectly.
EDIT:
Per some conversation below, I understand that for instance using a delegate layer can clearly show contravariance. Consider the following example
//legal, covariance
Mammal someMammal = new Mammal();
Animal someAnimal = someMammal;
// legal in C# 4.0, covariance (because defined in Interface)
IEnumerable<Mammal> mammalList = Enumerable.Empty<Mammal>();
IEnumerable<Animal> animalList = mammalList;
//because of this, one would assume
//that the following line is legal as well
void ProcessMammal(Mammal someMammal);
Action<Mammal> processMethod = ProcessMammal;
Action<Animal> someAction = processMethod;
Of course this is illegal because someone can pass any Animal to someAction, where as the ProcessMammal expects anything thats Mammal or more specific ( lesser than Mammal ). Which is why someAction has to only be Action or anything more specific (Action)
However this is introducing a layer of delegates in the middle, is it necessary that for a contravariant projection to happen there has to be a delegate in the middle? And if we were to define Process as an interface we would declare the argument parameter as a contravariant type only because we wouldn't want someone to be able to do what I had shown above with delegates?
public interface IProcess<out T>
{
void Process(T val);
}
See Question&Answers more detail:
os