C#列表<;接口>;:为什么你不能做'List<;IFoo>;foo=新列表<;酒吧>;();`

C#列表<;接口>;:为什么你不能做'List<;IFoo>;foo=新列表<;酒吧>;();`,c#,generics,C#,Generics,如果您有一个接口IFoo和一个类Bar:IFoo,为什么您可以执行以下操作: List<IFoo> foo = new List<IFoo>(); foo.Add(new Bar()); IEnumerable<IFoo> foo = new List<Bar>(); List foo=new List(); foo.Add(新条()); 但你不能这样做: List<IFoo> foo = new List<Bar>

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

List<IFoo> foo = new List<IFoo>();  
foo.Add(new Bar());
IEnumerable<IFoo> foo = new List<Bar>();
List foo=new List();
foo.Add(新条());
但你不能这样做:

List<IFoo> foo = new List<Bar>();
List foo=new List();

这与列表的创建有关,您已将T指定为IFoo,因此无法将其实例化为Bar,因为它们是不同的类型,即使Bar支持IFoo。

列表是本例中的类型,它不是一个继承问题列表,它实际上与列表不同。List不知道IFoo或Bar的任何内容,也不继承IFoo或Bar的特性


希望对您有所帮助。

List
不会继承自
List
如果您有类型为
List
的列表,可以调用
List.add(new Baz())假设
Baz
实现
IFoo
。但是,不能使用
列表
,因此不能在任何可以使用
列表的地方使用
列表

但是,由于
Bar
实现了
IFoo
,因此在使用
IFoo
的任何地方都可以使用一个Bar,因此在需要时传递一个Bar来添加工作,
IFoo

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

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


关于C#中协方差和反方差的更多背景知识,Eric Lippert有一个。

,因为
IFoo
s列表也可以包含一些
Bar
s,但是
IFoo
s列表与
Bar
s列表不同

请注意,我在上面使用了英语而不是C。我想强调的是,这不是一个深层次的问题;您只是被语法的细节弄糊涂了。要理解答案,您需要超越语法,思考它的实际含义

IFoo
s列表可以包含
Bar
,因为
Bar
也是
IFoo
。这里我们讨论的是列表中的元素。该列表仍然是
IFoo
s的列表。我们没有改变这一点


现在,您调用的
foo
列表仍然是
IFoo
s的列表(更学究的是,
foo
被声明为
list
)。这不可能是别的。特别是,不能将其制作成
的列表(
列表
)。
Bar
的列表与
IFoo
s的列表是完全不同的对象。

随便一看,这应该(就像啤酒应该是免费的一样)起作用。然而,一个快速的健康检查告诉我们为什么它不能。请记住,以下代码将不会编译。它的目的是显示为什么它是不允许的,即使它看起来不错,直到一点

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.

//.....
公共接口IFoo{}
公共类栏:IFoo{}
公共类Zed:IFoo{}
//.....
List myList=新列表();//到目前为止是有道理的
myList.Add(新条());//好的,因为Bar实现了IFoo
myList.Add(new Zed());//啊!现在我们明白为什么了。
//.....

myList
是一个
列表
,这意味着它可以采用
IFoo
的任何实例。但是,这与它被实例化为
List
的事实相冲突。由于拥有
列表
意味着我可以添加
Zed
的新实例,我们不能允许这样做,因为底层列表实际上是
List
,它不能容纳
Zed

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

using System.Linq;

---

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

List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
使用System.Linq;
---
列表栏=新列表();
添加(新条());
List foo=bar.OfType().ToList();

我使用了一些linq来简化转换

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

List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();
列表栏=新列表();
添加(新条());
List foo=bar.Select(x=>(IFoo)x.ToList();

与其他答案相比,它的难度要小一些,但效果很好

我相信.OfType()部分是多余的。我没有它也能逃脱。顺便问一下,在第一个例子中它是如何工作的?如果Bar1和Bar2都实现IFoo,您将能够向同一列表中添加新的Bar1()和新的Bar2()?很抱歉,该示例引发了相同的错误
无法将此行的类型“System.Collections.Generic.list”隐式转换为“System.Collections.Generic.list
@AhammadaliPK(以及其他阅读此评论的人):应该失败。答案文本中已经提到了这一点。该示例的重点是说明为什么这行代码不合逻辑,因此编译器不允许。它的目的不是展示如何去做,而是展示为什么不能去做!