【问题标题】:C# List<Interface>: why you cannot do `List<IFoo> foo = new List<Bar>();`C# List<Interface>:为什么你不能做 `List<IFoo> foo = new List<Bar>();`
【发布时间】:2009-08-04 15:29:16
【问题描述】:

如果你有一个接口IFoo和一个类Bar : IFoo,为什么你可以这样做:

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

但你不能这样做:

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

【问题讨论】:

  • 顺便说一句,这在第一个示例中是如何工作的?如果 Bar1 和 Bar2 都实现了 IFoo,您将能够在同一个列表中添加一个新的 Bar1() 和一个新的 Bar2()?

标签: c# generics


【解决方案1】:

乍一看,这应该(就像啤酒中的应该是免费的)似乎可以工作。但是,快速的健全性检查向我们展示了为什么它不能。请记住,以下代码不会编译。它旨在说明为什么不允许这样做,即使它看起来直到某一点为止都还不错。

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.

//.....

myListList&lt;IFoo&gt;,这意味着它可以采用IFoo 的任何实例。但是,这与它被实例化为List&lt;Bar&gt; 的事实相冲突。由于拥有List&lt;IFoo&gt; 意味着我可以添加Zed 的新实例,因此我们不能允许这样做,因为基础列表实际上是List&lt;Bar&gt;,它不能容纳Zed

【讨论】:

  • 抱歉这个例子抛出了同样的错误Cannot implicitly convert type 'System.Collections.Generic.List&lt;WebApplication1.Models.Bar&gt;' to 'System.Collections.Generic.List&lt;WebApplication1.Models.IFoo&gt;`对于这行List&lt;IFoo&gt; myList = new List&lt;Bar&gt;();
  • @AhammadaliPK(以及其他阅读此评论的人):它应该失败。答案文本中已经提到了这一点。该示例的重点是说明为什么该代码行不合逻辑,因此编译器不允许这样做。不是为了说明怎么做,而是为了说明为什么不能做!
【解决方案2】:

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

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

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

有关 C# 中协变和逆变的更多背景知识,Eric Lippert 有一个nice blog series

【讨论】:

    【解决方案3】:

    如果您需要将列表转换为基类或接口的列表,您可以这样做:

    using System.Linq;
    
    ---
    
    List<Bar> bar = new List<Bar>();
    bar.add(new Bar());
    
    List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
    

    【讨论】:

    • 我相信 .OfType() 部分是多余的。没有它我也能逃脱。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

      它比其他答案更简洁,但效果很好

      【讨论】:

        【解决方案6】:

        因为IFoos 列表也可以包含一些Bars,但IFoos 列表Bars 列表不同。

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

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

        现在,您称为foo 的列表仍然是IFoos 的列表(更迂腐,foo 被声明为List&lt;IFoo&gt;)。它不可能是别的东西。特别是,它不能成为Bars (List&lt;Bar&gt;) 的列表。 Bar 列表与IFoos 列表是完全不同的对象。

        【讨论】:

          【解决方案7】:

          如果你有一个List&lt;IFoo&gt; 类型的列表,你可以调用list.add(new Baz());,假设Baz 实现了IFoo。但是,您不能使用 List&lt;Bar&gt; 来做到这一点,因此您不能在任何可以使用 List&lt;IFoo&gt; 的地方使用 List&lt;Bar&gt;

          但是,由于Bar 实现了IFoo,因此您可以在使用IFoo 的任何地方使用 Bar,因此传递 Bar 以在预期和 IFoo 时添加工作。

          【讨论】:

            【解决方案8】:

            List 是这种情况下的类型,它不是继承问题 List 确实与 List 不同。 List 不知道或继承 IFoo 或 Bar 的任何特征。

            希望对您有所帮助。

            【讨论】:

              【解决方案9】:

              List&lt;Bar&gt; 不继承自 List&lt;IFoo&gt;

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-01-25
                • 1970-01-01
                • 2019-03-15
                • 2010-11-13
                相关资源
                最近更新 更多