【问题标题】:Why does IEnumerable<T> inherit from IEnumerable?为什么 IEnumerable<T> 继承自 IEnumerable?
【发布时间】:2008-10-21 12:46:33
【问题描述】:

这可能是一个老问题:为什么IEnumerable&lt;T&gt; 继承自IEnumerable?

.NET 就是这样做的,但它带来了一些麻烦。每次写一个类实现IEumerable&lt;T&gt;,就得写两个GetEnumerator()函数,一个给IEnumerable&lt;T&gt;,另一个给IEnumerable。

而且,IList&lt;T&gt; 不继承自 IList。

我不知道为什么IEnumerable&lt;T&gt; 会以其他方式设计。

【问题讨论】:

    标签: c# generics ienumerable


    【解决方案1】:

    Straight from the horse's mouth(海尔斯伯格):

    理想情况下,所有泛型集合接口(例如ICollection&lt;T&gt;、IList&lt;T&gt;)都将从它们的非泛型对应物继承,以便泛型接口实例可以用于泛型和非泛型代码。例如,如果可以将 IList&lt;T&gt; 传递给需要 IList 的代码,那将会很方便。

    事实证明,唯一可能的通用接口是IEnumerable&lt;T&gt;,因为只有IEnumerable&lt;T&gt; 是逆变的:在IEnumerable&lt;T&gt; 中,类型参数T 仅用于“输出”位置(返回值)而不是在“输入”位置(参数)。 ICollection&lt;T&gt; 和 IList&lt;T&gt; 在输入和输出位置都使用 T,因此这些接口是不变的。 (顺便说一句,如果 T 仅用于输入位置,它们将是逆变的,但这并不重要。)

    <...>

    所以,为了回答您的问题,IEnumerable&lt;T&gt; 继承自 IEnumerable,因为它可以! :-)

    【讨论】:

    • 这很有趣...我真的很生气IList&lt;T&gt; 没有继承IList,但是一旦您考虑到差异,它就开始有意义...
    • 可惜微软从来没有定义 IReadableByIndex 和 IReadableByIndex(Of Out T),它们本来可以被 IList 和 IList(Of T) 继承。 IReadableByIndex 可以毫无困难地被 IReadableByIndex(Of T) 继承,并且只读可索引集合的方差会非常好。
    • @supercat Microsoft 可能已经听到了你的祈祷,因为自从你写了那条评论后,他们就引入了IReadOnlyList&lt;out T&gt;,其中out 表示T 中的协方差。
    • @JeppeStigNielsen:他们确实添加了该接口,但不幸的是,IList&lt;T&gt; 实现没有自动实现它的机制,也没有任何方法可以让代码确定一个对象是否实现了@987654340 @offers 是一个可变列表,一个只读视图(不能转换为可变)到一个可能在外部更改的列表,一个永远不应该改变的列表的视图,或者一个可以保证的列表永不改变。
    • 让我烦恼的是我必须实现的IEnumerator IEnumerable.GetEnumerator() 方法。它没有访问器,VS 标记是私有的,无论我尝试什么,它都不会被调用。即使做IEnumerator iter = intlist.GetEnumerator(); 也会调用通用公共方法。如果我把throw new NotImplementedException();留在里面会不会有什么不好的事情发生?
    【解决方案2】:

    IEnumerable 的答案是:“因为它可以在不影响类型安全的情况下”。

    IEnumerable 是一个“只读”接口 - 所以泛型形式比非泛型形式更具体并不重要。你不会通过同时实现两者来破坏任何东西。 IEnumerator.Current 返回 object,而 IEnumerator&lt;T&gt;.Current 返回 T - 没关系,因为您始终可以合法地转换为 object,尽管这可能意味着拳击。

    将此与IList&lt;T&gt; 和IList 进行比较-您可以在IList 上调用Add(object),而这对于任何特定的IList&lt;T&gt; 都可能无效(实际上是IList&lt;object&gt; 以外的任何东西)。

    Brad Abram 的 blogged with Anders' answer 关于这个问题。

    【讨论】:

      【解决方案3】:

      这是为了向后兼容。如果您调用需要普通 IEnumerable 的 .Net 1.1 函数,则可以传入通用 IEnumerable。

      幸运的是,通用 IEnumerator 继承自旧式 IEnumerator

      我通常实现一个返回枚举器的私有方法,然后将它传递给旧式和新式 GetEnumerator 方法。

          private IEnumerator<string> Enumerator() {
              // ...
          }
      
          public IEnumerator<string> GetEnumerator() {
              return Enumerator();
          }
      
          System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
              return Enumerator();
          }
      

      【讨论】:

      • 在实现 IEnumerable 时,您还需要实现私有 IEnumerable.GetEnumerator() 方法。幸运的是,上面的代码做得很好,所以你没有重复工作。
      • @spoulson:不是私人的。它是接口的一部分,因此必须公开实现。 Mendelt 的示例并没有私下实现它,而是显式地实现了它,因此它只有在对象被强制转换为 IEnumerable 时才可用。
      • @spoulson:后两个方法是我在visual studio中点击实现界面时生成的,没仔细看。您可以添加一个 using for System.Collections 并将第三个函数显式公开。
      【解决方案4】:

      这样它就可以与不支持泛型的类一起使用。此外,.NET 泛型不允许您执行诸如将 IList 转换为 IList 之类的操作,因此当您需要固定的基类或接口时,接口的非泛型版本可能非常有用。

      【讨论】:

      • 这一点很重要。 IList 不能转换为 IList。
      猜你喜欢
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多