通用协方差和逆变

Ash*_*ohn 16 c# generics covariance contravariance

考虑代码片段.

IList<String> obj=new List<string>();
IEnumerable<Object> obj1 = obj;
Run Code Online (Sandbox Code Playgroud)

但如果我写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);
}
Run Code Online (Sandbox Code Playgroud)

dle*_*lev 20

ICollection<T>在类型参数上不是协变的,而是IEnumerable<T>.如果查看它们的声明(ICollection,IEnumerable),您可以看到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>
Run Code Online (Sandbox Code Playgroud)

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