如果您有界面IFoo
和课程Bar : IFoo
,为什么还要执行以下操作:
List<IFoo> foo = new List<IFoo>();
foo.Add(new Bar());
但你不能这样做:
List<IFoo> foo = new List<Bar>();
答案 0 :(得分:112)
随便看一眼,这个应该(如啤酒应该是免费的)工作。但是,快速的健全检查会告诉我们为什么不能这样做。请记住,以下代码将无法编译。它旨在说明为什么它不被允许,即使看起来直到一个点。
public interface IFoo { }
public class Bar : IFoo { }
public class Zed : IFoo { }
//.....
List<IFoo> myList = new List<Bar>(); // makes sense so far
myList.Add(new Bar()); // OK, since Bar implements IFoo
myList.Add(new Zed()); // aaah! Now we see why.
//.....
myList
是List<IFoo>
,这意味着它可以占用IFoo
的任何实例。但是,这与它被实例化为List<Bar>
的事实相冲突。由于拥有List<IFoo>
意味着我可以添加Zed
的新实例,因此我们无法允许,因为基础列表实际上是List<Bar>
,不能容纳{{1} }}
答案 1 :(得分:38)
原因是C#不支持C#3.0或更早版本中泛型的共同和逆转。这是在C#4.0中实现的,因此您将能够执行以下操作:
IEnumerable<IFoo> foo = new List<Bar>();
请注意,在C#4.0中,您可以强制转换为IEnumerable&lt; IFoo&gt;,但您无法转换为List&lt; IFoo&gt;。原因是由于类型安全,如果你能够施放List&lt; Bar&gt;列出&lt; IFoo&gt;你可以在列表中添加其他IFoo实现者,打破类型安全。
有关C#中协方差和逆变的更多背景知识,Eric Lippert有一个nice blog series。
答案 2 :(得分:9)
如果需要将列表转换为基类或接口列表,可以执行以下操作:
using System.Linq;
---
List<Bar> bar = new List<Bar>();
bar.add(new Bar());
List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
答案 3 :(得分:4)
与List的创建有关,你已经指定T为IFoo,因此你不能将它实例化为Bar,因为它们是不同的类型,即使Bar支持IFoo。
答案 4 :(得分:1)
List是这种情况下的类型,它不是继承问题List&lt; IFoo&gt;真的与List&lt; Bar&gt;不同。 List不知道任何标志,或继承IFoo或Bar的特征。
希望有所帮助。
答案 5 :(得分:1)
List<Bar>
不会继承List<IFoo>
答案 6 :(得分:1)
因为IFoo
的列表也可以包含一些Bar
,但IFoo
s 的列表与列表不同 Bar
s。
请注意,我使用上面的英语而不是使用C#。我想强调这不是一个深层问题;你只是对语法的细节感到困惑。要理解答案,您需要超越语法并思考它的实际含义。
IFoo
的列表可以包含Bar
,因为Bar
也是IFoo
。这里我们谈论列表的元素。该列表仍然是IFoo
的列表。我们没有改变它。
现在,您调用foo
的列表仍然是IFoo
的列表(更迂腐,foo
被声明为List<IFoo>
)。它不可能是其他任何东西。特别是,它不能成为Bar
s(List<Bar>
)的列表。 Bar
列表与IFoo
s列表完全不同。
答案 7 :(得分:1)
我使用了一些linq来简化转换
List<Bar> bar = new List<Bar>();
bar.add(new Bar());
List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();
它比其他答案略显简洁,但运作良好
答案 8 :(得分:0)
如果您有List<IFoo>
类型的列表,则可以致电list.add(new Baz());
,假设Baz
实施IFoo
。但是,您无法使用List<Bar>
执行此操作,因此无法在List<Bar>
处使用List<IFoo>
。
但是,由于Bar
实现了IFoo
,您可以在使用IFoo
的任何位置使用条形码,因此通过条形图添加可以在预期时使用IFoo
。