Search code examples
c#genericscovariancecontravariance

Is covariance all about accepting values?


Is covariance all about accepting values?

I am learning the C# through the CLR via C# book. And I come across the following excerpt:

Since T is covariant, it is possible to have the following code compile and run successfully:

// This method accepts an IEnumerable of any reference type
Int32 Count(IEnumerable<Object> collection) { ... }

...
// The call below passes an IEnumerable<String> to Count
Int32 c = Count(new[] { "Grant" }); 

I am confused here. Because the covariance is about having the type which is one of the base types of the required type. And as such the covariance is used only in the context of the return types. While in the example above we have a String (which is derived from Object, so that is contravariant, but not covariant) which is used in the context of passing arguments (but not returning values).

So, should we use in the example above the contravariant instead of the covariant (meaning that there is an error in the book)?

UPDATE

After the comments I got another question. Are the following definitions correct?

Contravariant Meaning that the generic type parameter can change from a class to a class derived from it. In C#, you indicate contravariant generic type parameters with the in keyword. Contravariant generic type parameters can appear only in input positions such as a method’s argument.

Covariant Meaning that the generic type argument can change from a class to one of its base classes. In C#, you indicate covariant generic type parameters with the out keyword. Covariant


Solution

  • As Josh pointed out the book is correct.

    You can check this link if you want to confirm it from another source.

    IEnumerable<Cat> is a subtype of IEnumerable<Animal>. The subtyping is preserved because IEnumerable<T> is covariant on T.

    The two keywords in C# for those concepts are out for covariant and in for contravariant. In the IEnumerable<T> case this translate to IEnumerable<out T>.

    Hope this helps.

    UPDATE

    You would have to inverse your definitions as follows.

    Covariant Meaning that the generic type parameter can be a certain class and all the derived classes from it (IEnumerable<Object> can be a IEnumerable<String> since String is a subtype of Object). In C#, you indicate covariant generic type parameters with the out keyword.

    Contravariant Meaning that the generic type argument can change from a class to one of its base classes. In C#, you indicate contravariant generic type parameters with the in keyword.