是否可以在C#4.0中投放List<Subclass>
到List<Superclass>
?
这些方面的东西:
class joe : human {}
List<joe> joes = GetJoes();
List<human> humanJoes = joes;
这不是协方差的意思吗?
如果可以的话:
human h = joe1 as human;
为什么你不能做到
List<human> humans = joes as List<human>;
比做人([joe]人类[0]不合法,因为该项目已被降级......每个人都会很开心。现在唯一的选择是创建一个新的列表
答案 0 :(得分:26)
你不能这样做,因为它不安全。考虑:
List<Joe> joes = GetJoes();
List<Human> humanJoes = joes;
humanJoes.Clear();
humanJoes.Add(new Fred());
Joe joe = joes[0];
显然,最后一行(如果不是较早的那行)有失败 - 因为Fred
不是Joe
。 List<T>
的不变性可以防止编译时的错误,而不是执行时间。
答案 1 :(得分:6)
实例化一个以joes为输入的新人员列表:
List<human> humanJoes = new List<human>(joes);
答案 2 :(得分:2)
没有。 C#4.0的协/反变量功能仅支持接口和委托。不支持List<T>
等具体类型。
答案 3 :(得分:1)
没有。正如Jared所说,C#4.0的协同/反向变换功能仅支持接口和委托。但是它也不适用于IList<T>
,原因是IList<T>
包含添加和更改列表中项目的方法 - 正如Jon Skeet的新答案所说。
能够将“joe”列表转换为“human”的唯一方法是,如果界面纯粹是设计的只读,如下所示:
public interface IListReader<out T> : IEnumerable<T>
{
T this[int index] { get; }
int Count { get; }
}
即使是Contains(T item)
方法也是不允许的,因为当您将IListReader<joe>
投射到IListReader<human>
时,Contains(human item)
中没有IListReader<joe>
方法。
您可以使用GoInterface“强制”从IList<joe>
转换为IListReader<joe>
,IListReader<human>
甚至IList<human>
。但是如果列表足够小可以复制,那么更简单的解决方案就是将其复制到新的List<human>
中,正如Paw指出的那样。
答案 4 :(得分:0)
如果我允许您将List<Joe> joes
概括为......
List<Human> humans = joes;
...现在,两个引用humans
和joes
指向完全相同的列表。上述分配后的代码无法阻止将另一种类型的人的实例(例如水管工)插入/添加到列表中。鉴于class Plumber: Human {}
humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe
humans
引用的列表现在包含了joes和管道工。请注意,引用joes
仍引用相同的列表对象。 现在如果我使用引用joes
从列表对象中读取,我可能会弹出一个管道工而不是一个乔。不知道水管工和乔是否可以隐含地相互转换......所以我从管道中找到一个水管工而不是一个乔会打破类型安全。通过参考一系列的工作,肯定不欢迎水管工。
然而,在最近的C#版本中,通过实现类型参数上有out
修饰符的通用接口,可以解决泛型类/集合的这种限制。假设我们现在有ABag<T> : ICovariable<out T>
。 out修饰符仅限制T到输出位置(例如,方法返回类型)。你不能在袋子里输入任何T.你只能读出它们。 这允许我们将joes推广到ICovariable<Human>
,而不必担心将Plumber插入其中,因为界面不允许这样做。我们现在可以写...
ICovariable<Human> humans = joes ; // now its good !
humans.Add(new Plumber()); // error