简体   繁体   English

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

[英]C# List<Interface>: why you cannot do `List<IFoo> foo = new List<Bar>();`

If you have an Interface IFoo and a class Bar : IFoo , why can you do the following: 如果您有一个接口IFoo和一个类Bar : IFoo ,为什么Bar : IFoo执行以下操作:

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

But you cannot do: 但你做不到:

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

At a casual glance, it appears that this should (as in beer should be free) work. 随便看一眼,这似乎应该 (如在啤酒应该是免费的)工作。 However, a quick sanity check shows us why it can't. 但是,快速的健全检查会告诉我们为什么不能这样做。 Bear in mind that the following code will not compile . 请记住,以下代码将无法编译 It's intended to show why it isn't allowed to, even though it looks alright up until a point. 它旨在说明为什么它不被允许,即使它直到某一点看起来还不错。

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 is a List<IFoo> , meaning it can take any instance of IFoo . myListList<IFoo> ,这意味着它可以使用IFoo任何实例。 However, this conflicts with the fact that it was instantiated as List<Bar> . 但是,这与它被实例化为List<Bar>的事实相冲突。 Since having a List<IFoo> means that I could add a new instance of Zed , we can't allow that since the underlying list is actually List<Bar> , which can't accommodate a Zed . 由于有一个List<IFoo>意味着我可以添加一个新的Zed实例,我们不能允许,因为底层列表实际上是List<Bar> ,它不能容纳Zed

The reason is that C# does not support co- and contravariance for generics in C# 3.0 or earlier releases. 原因是C#不支持C#3.0或更早版本中泛型的共同和逆变。 This is being implemented in C# 4.0, so you'll be able to do the following: 这是在C#4.0中实现的,因此您将能够执行以下操作:

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

Note that in C# 4.0, you can cast to IEnumerable<IFoo>, but you won't be be able cast to List<IFoo>. 请注意,在C#4.0中,您可以强制转换为IEnumerable <IFoo>,但是您无法转换为List <IFoo>。 The reason is due to type safety, if you were able to cast a List<Bar> to List<IFoo> you would be able to add other IFoo implementors to the list, breaking type safety. 原因是类型安全,如果你能够将List <Bar>转换为List <IFoo>,你可以将其他IFoo实现者添加到列表中,从而破坏类型安全性。

For more background on covariance and contravariance in C#, Eric Lippert has a nice blog series . 有关C#中协方差和逆变的更多背景知识,Eric Lippert有一个很好的博客系列

If you need to convert a list to a list of a base class or interface you can do this: 如果需要将列表转换为基类或接口的列表,可以执行以下操作:

using System.Linq;

---

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

List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();

它与List的创建有关,你已经指定T为IFoo,因此你不能将它实例化为Bar,因为它们是不同的类型,即使Bar支持IFoo。

List is the type in this case and it's not an inheritance question List<IFoo> really is different than List<Bar>. List是这种情况下的类型,它不是继承问题List <IFoo>确实与List <Bar>不同。 List doesn't know anythign of, or inherit the characteristics of either IFoo or Bar. 列表不知道任何标志,或继承IFoo或Bar的特征。

Hope that helps. 希望有所帮助。

List<Bar>不从List<IFoo>继承

Because a list of IFoo s can contain some Bar s as well, but a list of IFoo s is not the same thing as a list of Bar s. 由于名单IFoo S能包含一些Bar S作为很好,但名单IFoo s 是不一样的事情的清单Bar秒。

Note that I used English above instead of using C#. 请注意,我使用上面的英语而不是使用C#。 I want to highlight that this is not a deep problem; 我想强调这不是一个深层问题; you are just getting confused by the details of the syntax. 你只是对语法的细节感到困惑。 To understand the answer you need to see beyond the syntax and think about what it actually means. 要理解答案,您需要超越语法并思考它的实际含义。

A list of IFoo s can contain a Bar , because a Bar is an IFoo as well. IFoo列表可以包含一个Bar ,因为Bar也是一个IFoo Here we're talking about the elements of the list. 这里我们谈论列表的元素。 The list is still a list of IFoo s. 该列表仍然是IFoo的列表。 We haven't changed that. 我们没有改变这一点。

Now, the list you called foo is still a list of IFoo s (more pedantically, foo is declared as a List<IFoo> ). 现在,你调用foo的列表仍然是IFoo的列表(更迂腐, foo被声明为List<IFoo> )。 It cannot be anything else. 它不可能是其他任何东西。 In particular, it cannot be made into a list of Bar s ( List<Bar> ). 特别是,它不能成为BarList<Bar>List<Bar> )。 A list of Bar is a completely different object than a list of IFoo s. Bar列表与IFoo列表完全不同。

I used some linq to make the conversion easier 我使用了一些linq来简化转换

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

List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();

It is a little less versbose than other answers but works well 它比其他答案略显简洁,但运作良好

If you have a list of type List<IFoo> you can call list.add(new Baz()); 如果你有List<IFoo>类型的List<IFoo>你可以调用list.add(new Baz()); assuming Baz implements IFoo . 假设Baz实现了IFoo However you can't do that with a List<Bar> , so you can't use a List<Bar> everywhere you can use a List<IFoo> . 但是,您无法使用List<Bar> ,因此您无法在任何地方使用List<Bar>来使用List<IFoo>

However since Bar implements IFoo , you can use a Bar everywhere you use IFoo , so passing a Bar to add works when it expects and IFoo . 但是,由于Bar实现了IFoo ,你可以在任何地方使用一个使用IFoo的Bar,所以传递一个Bar可以在它预期和IFoo时添加。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM