C#List <Interface>:为什么你不能做`List <IFoo> foo = new List <Bar>();`

new*_*ewB 53 c# generics

如果您有一个接口IFoo和一个类Bar : IFoo,为什么可以执行以下操作:

List<IFoo> foo = new List<IFoo>();  
foo.Add(new Bar());
Run Code Online (Sandbox Code Playgroud)

但你做不到:

List<IFoo> foo = new List<Bar>();
Run Code Online (Sandbox Code Playgroud)

Ada*_*son 119

随便看一眼,这应该(如在啤酒应该是免费的)工作.但是,快速的健全检查会告诉我们为什么不能这样做.请记住,以下代码将无法编译.它旨在说明为什么它不被允许,即使它直到某一点看起来还不错.

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.

//.....
Run Code Online (Sandbox Code Playgroud)

myList是一个List<IFoo>,意思是它可以采取任何实例IFoo.但是,这与它被实例化为的事实相冲突List<Bar>.由于List<IFoo>我有一个可以添加新实例的方法Zed,我们不能允许,因为底层列表实际上是List<Bar>无法容纳的Zed.

  • @AhammadaliPK(以及阅读此评论的其他人):**应该**失败。这在答案文本中已经提到过。该示例的目的是说明为什么该行代码不合逻辑,因此不被编译器允许。它的目的不是展示如何做到这一点,而是展示为什么不能做到这一点! (2认同)

Dus*_*ell 39

原因是C#不支持C#3.0或更早版本中泛型的共同和逆变.这是在C#4.0中实现的,因此您将能够执行以下操作:

IEnumerable<IFoo> foo = new List<Bar>();
Run Code Online (Sandbox Code Playgroud)

请注意,在C#4.0中,您可以强制转换为IEnumerable <IFoo>,但是您无法转换为List <IFoo>.原因是由于类型安全,如果您能够将List <Bar>转换为List <IFoo>,您将能够将其他IFoo实现者添加到列表中,从而破坏类型安全性.

有关C#中协方差和逆变的更多背景知识,Eric Lippert有一个很好的博客系列.


小智 10

如果需要将列表转换为基类或接口的列表,可以执行以下操作:

using System.Linq;

---

List<Bar> bar = new List<Bar>();
bar.add(new Bar());

List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
Run Code Online (Sandbox Code Playgroud)

  • 我相信 .OfType&lt;IFoo&gt;() 部分是多余的。没有它我就可以逃脱。 (2认同)