Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Covariance and contravariance [duplicate]

Consider the code snippet.

IList<String> obj=new List<string>();
IEnumerable<Object> obj1 = obj;

But if i write ICollection<Object> obj2 = obj; it throws me a compile time error.

Cannot implicitly convert type 'System.Collections.Generic.IList<string>' to 'System.Collections.Generic.ICollection<object>'.

Why is this behavior since List<T> implements both IEnumerable<T> and ICollection<T> and also IList<T> is defined as

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
    T this[int index] { get; set; }
    int IndexOf(T item);
    void Insert(int index, T item);
    void RemoveAt(int index);
}
like image 763
Ashley John Avatar asked Jul 16 '11 13:07

Ashley John


People also ask

What is covariance and contravariance in generic?

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.

What is the difference between covariance and contravariance?

In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.

What is covariance and contravariance in Scala?

Covariance allows assigning an instance to a variable whose type is one of the instance's generic type; i.e. supertype. Contravariance allows assigning an instance to a variable whose type is one of the instance's derived type; i.e. subtype.

How do you declare a generic interface as contravariant?

You can declare a generic type parameter contravariant by using the in keyword. The contravariant type can be used only as a type of method arguments and not as a return type of interface methods. The contravariant type can also be used for generic constraints.


2 Answers

ICollection<T> is not covariant on the type parameter, whereas IEnumerable<T> is. If you look at their declarations (ICollection, IEnumerable) you can see that IEnumerable<T> uses the out keyword on T, while ICollection<T> does not.

This makes sense if you think about it, since (roughly speaking) covariance is safe when the interface will only be used to read objects (and thus the out keyword). IEnumerable<T> clearly meets that criterion, whereas ICollection<T> is quite the opposite.

As an example of what could go wrong (using your example):

IList<String> obj = new List<string>(); // Legal, of course
ICollection<Object> obj1 = obj;         // Illegal, but let's see what happens
obj1.Add(new NonStringObject());        // That's not a string being stored in a List<string>

Remember: covariance is not the same as inheritance. Just because two classes or interfaces share an inheritance relation does not mean their type parameters share the same variance characteristics.

like image 137
dlev Avatar answered Oct 02 '22 20:10

dlev


The key here is whether the collection is modifiable. IEnumerable<T> is a read-only collection of Ts, whereas ICollection<T> supports Add. A modifiable collection cannot be covariant, because:

IList<String> obj = new List<String>();
ICollection<Object> obj1 = obj;
obj1.Add(new Elephant());

This would typecheck, since (presumably) Elephant is a subclass of Object. But now obj, which is a List<string> has an Elephant as its last element, which is clearly a bad thing.

like image 23
luqui Avatar answered Oct 02 '22 20:10

luqui