16

考虑代码片段。

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

2 回答 2

20

ICollection<T>在类型参数上不是协变的,而是IEnumerable<T>。如果您查看他们的声明(ICollectionIEnumerable),您会看到IEnumerable<T>使用out关键字 on 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>

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

于 2011-07-16T13:48:04.367 回答
4

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

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

这将进行类型检查,因为(大概)ElephantObject. 但是现在obj,这是 aList<string>Elephant最后一个元素,这显然是一件坏事。

于 2011-07-16T13:53:11.690 回答