C#List <Interface>:为什么你不能`List <IFoo> foo = new List <Bar>();`
如果你有一个接口IFoo
和一个Bar : IFoo
类Bar : IFoo
,你为什么能做到以下几点:
List<IFoo> foo = new List<IFoo>(); foo.Add(new Bar());
但是你不能这样做:
List<IFoo> foo = new List<Bar>();
匆匆一瞥,似乎应该 (如啤酒应该是免费的)工作。 但是,快速的完整性检查显示了为什么它不能。 请记住,下面的代码不会编译 。 这是为了说明为什么它不被允许,即使它看起来没问题 ,直到一个点。
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>
,它不能容纳一个Zed
。
原因是C#不支持C#3.0或更早发行版中的generics和协变性。 这是在C#4.0中实现的,所以你可以做到以下几点:
IEnumerable<IFoo> foo = new List<Bar>();
请注意,在C#4.0中,您可以强制转换为IEnumerable <IFoo>,但不能强制转换为List <IFoo>。 原因是由于types安全性,如果您能够将List <Bar>转换为List <IFoo>,则可以将其他IFoo实现者添加到列表中,从而打破types安全性。
有关C#中协变和逆变的更多背景,Eric Lippert有一个很好的博客系列 。
如果您需要将列表转换为基类或接口的列表,您可以这样做:
using System.Linq; --- List<Bar> bar = new List<Bar>(); bar.add(new Bar()); List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
这是创build列表,你已经指定T为IFoo,因此你不能将它实例化为一个Bar,因为它们是不同的types,即使Bar支持IFoo。
List是这种情况下的types,它不是一个inheritance问题List <IFoo>确实不同于List <Bar>。 列表不知道任何关系,或者inheritanceIFoo或Bar的特性。
希望有所帮助。
因为IFoo
的列表也可以包含一些Bar
,但是IFoo
的列表与Bar
列表不是一回事。
请注意,我使用上面的英文,而不是使用C#。 我想强调这不是一个深刻的问题; 你只是对语法的细节感到困惑。 要理解答案,你需要看到超越语法,并思考它的实际意义。
IFoo
的列表可以包含一个Bar
,因为Bar
也是一个IFoo
。 我们在这里讨论列表的元素。 这个清单仍然是IFoo
的清单。 我们没有改变这一点。
现在,你称为foo
的列表仍然是IFoo
的列表(更迂回地说, foo
被声明为List<IFoo>
)。 它不能是其他的东西。 特别是,它不能被制作成Bar
的List<Bar>
( List<Bar>
)。 Bar
列表与IFoo
列表完全不同。
List<Bar>
不会从List<IFoo>
inheritance
如果你有一个List<IFoo>
types的List<IFoo>
你可以调用list.add(new Baz());
假设Baz
实施IFoo
。 然而,你不能用List<Bar>
来做到这一点,所以你不能在任何你可以使用List<IFoo>
地方使用List<Bar>
List<IFoo>
。
然而,因为Bar
实现了IFoo
,所以你可以在任何你使用IFoo
地方使用Bar,所以当它期望和IFoo
时,通过一个Bar来添加工作。
我使用了一些linq来使转换更容易
List<Bar> bar = new List<Bar>(); bar.add(new Bar()); List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();
这比其他答案less一点,但效果很好