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);
}
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.
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.
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.
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.
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.
The key here is whether the collection is modifiable. IEnumerable<T>
is a read-only collection of T
s, 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With