Let's examine what invariant, covariant, and contravariant parameters and return types mean. You almost certainly are familiar with the terms, even if you don't have a grasp of the formal definitions.
A return value or parameter is invariant if you must use the exact match of the formal type name. A parameter is covariant if you can use a more derived type as a substitute for the formal parameter type. A return value is contravariant if you can assign the return type to a variable of a less derived type than the formal parameter.
In most cases, C# supports covariant parameters and contravariant return types. That's consistent with almost every other object-oriented language. In fact, polymorphism is usually built around the concepts of covariance and contravariance. You intuitively know that you can pass a derived class object to any method expecting a base class object. You intuitively know that you can pass a derived object to any method expecting a base object. After all, the derived object is also an instance of the base object. You instinctively know that you can store the result of a method in a variable of a less-derived object type than the formal method return type.
Read entire article