通用协方差和逆变

时间:2011-07-16 13:46:48

标签: c# generics covariance contravariance

考虑代码段。

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

但如果我写ICollection<Object> obj2 = obj;,它会抛出编译时错误。

  

无法将类型“System.Collections.Generic.IList<string>”隐式转换为“System.Collections.Generic.ICollection<object>”。

为什么此行为自List<T>同时实现IEnumerable<T>ICollection<T>以及IList<T>定义为

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);
}

2 个答案:

答案 0 :(得分:20)

ICollection<T>在类型参数上不是协变的,而IEnumerable<T>是。IEnumerable<T>。如果您查看他们的声明(ICollectionIEnumerable),您会发现out使用T上的ICollection<T>关键字,out

如果您考虑它,这是有道理的,因为(粗略地说)当界面仅用于读取对象(因此IEnumerable<T>关键字)时,协方差是安全的。 ICollection<T>明显符合这一标准,而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> 恰恰相反。

作为可能出错的一个例子(使用你的例子):

{{1}}

请记住:协方差与继承不同。仅仅因为两个类或接口共享一个继承关系并不意味着它们的类型参数共享相同的方差特征。

答案 1 :(得分:4)

这里的关键是集合是否可以修改。 IEnumerable<T>T只读集合,而ICollection<T>支持Add。可修改的集合不能协变,因为:

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

这将是类型检查,因为(推测)ElephantObject的子类。但是现在obj List<string>有一个Elephant作为最后一个元素,这显然是一件坏事。