Covariance and contravariace are the terms originated from Physics to explain how the scale of the factor can affect the scale of the outcome. Covariant is indicative of the scale of outcome is directly proportional to the scale of the cause whereas contravariant is inversely proportional.
In Programming, covariance and contravariance compliments the Liskov Substitution Principle of the SOLID Principles.
Simply put, contravariance is the conversion of a derived entity to its parent entity that are higher in the hierarchy. An epitome would converting Cat to Animal (upcasting).

Covariance on the other hand, is the conversion of a higher entity to a more derived, specific ones down the hierarchy. It is like converting Animal to Cat (downcasting).
Basics
Take the Animal and Cat class as example.
class Animal {
public void Eat() => Console.WriteLine("nom, nom");
}
class Cat : Animal {
public void Meow() => Console.WriteLine("meow");
}The base class Animal is used for both reference when instantiating both the Animal and Cat object.
Animal x = new Animal();
Animal y = new Cat();Both x and y are able to invoke the Eat method. However, since the Meow method only exist in the Cat class, a Cat object that is referenced by the base Animal class cannot invoke that method.
x.Meow(); // compilation error
y.Meow(); // compilation errorOnly the Cat object that has its own class as reference is able to access the Meow method.
Cat z = new Cat();
z.Meow();Generics
When it comes to generic typed interfaces, variance is mainly concerned with how objects can be substituted.
Covariant interface of type T indicates that its derivatives will only have methods that produce entities of type T. It is specified with the out keyword.
// covariant
interface IProducer<out T> {
T Produce();
}Conversely, contravariant interface of type T shows that its derivatives will only have methods that takes in object parameters of type T but not returning it. It is indicated by the in keyword.
// contravariant
interface IConsumer<in T> {
void Consume(T obj);
}Covariance
For example, a producer of type IProducer<Animal> cannot directly assign the return value to the Cat variable because Produce() returns Animal.
IProducer<Animal> producer;
Animal a = producer.Produce();
Cat b = producer.Produce(); // errorConversely, if it's IProducer<Cat>, Produce() returns Cat, which can also be assigned to a variable of type Animal.
IProducer<Cat> producer;
Animal c = producer.Produce();
Cat d = producer.Produce(); // no issueThis means that it is safe for a subtype to be placed in the position of a supertype, and this is called covariance.
Cat : Animal ==> IProducer<Cat> : IProducer<Animal>Contravariance
IConsumer<Animal> can consume not only Animal but also Cat.
IConsumer<Animal> consumer;
consumer.Consume(new Animal());
consumer.Consume(new Cat()); // no problemConversely, IConsumer<Cat> can only safely consume Cat and will throw an error if passed Animal.
IConsumer<Cat> consumer;
consumer.Consume(new Cat());
consumer.Consume(new Animal()); // errorHence, they implies contravariance as they behaves in the opposite way.
Cat : Animal ==> IConsumer<Animal> : IConsumer<Cat>Invariant Type
If a generic parameter does not have a mutation notation such as in or out, the type is invariant, meaning it does not allow substitution between subtypes.