【问题标题】:What is the reason for IEnumerable/IEnumerable<T> interfaces to only have MoveNext?IEnumerable/IEnumerable<T> 接口只有 MoveNext 的原因是什么?
【发布时间】:2010-12-20 20:04:22
【问题描述】:

基本上我想知道为什么 MS 决定实现一个只支持前进的枚举器:MoveNext()

为整个 .NET 框架中广泛使用的接口强制执行 MovePrevious 不是更灵活吗?

我可以想象它使 Linq.Reverse 更容易为 MS 实现并且性能更高效,但我不确定这是否会使其他事情变慢或对其他事情造成巨大的开销。

请在这方面有更多知识的人可以提供更多信息吗?即在IEnumerable/IEnumerable&lt;T&gt; 中拥有或不拥有MovePrevious 的利弊。

【问题讨论】:

    标签: c# .net ienumerable base-class-library


    【解决方案1】:

    实现 MovePrevious 会使界面更加更重。虽然对于某些源(数组、容器类)来说,MovePrevious 是微不足道的,但对于许多其他源,它需要昂贵的缓冲,或者排除这些源。网络流和数据库连接不支持 Seek 操作。

    【讨论】:

      【解决方案2】:

      在设计界面时,使用便利性并不是唯一需要考虑的因素……您必须考虑它的通用性以及您添加的限制。

      有些序列无法重播,并且您会添加许多不必要的要求来实现它。

      除此之外还有其他接口,如IList、IQueryable。使用最适合场景的,也传达了它应该具有的使用类型。

      【讨论】:

        【解决方案3】:

        这与“收益”的模式有关。实际上,枚举器是最简单的集合,您只需从前面开始,一直到最后。这意味着他们实现枚举的代码非常简单。

        您还可以使用永远不会“结束”的迭代器来编写功能代码,例如

        yield value++;
        

        您只会收到递增的数字,直到您停止。

        【讨论】:

        • 事实上根本不需要“结束”。考虑while (true) { yield return Random.Next(); }
        • IEnumerator 的设计比产量早几年。
        • yield 只是让它更容易实现,但(“最简单的集合”)的概念同样古老。不过,措辞可以更好。
        • 我使用我们在语言方面取得的进步来描述这个概念的最佳方式。 .Net 中的许多集合概念是几年前发明的,在某些情况下是几十年前发明的。不过,在描述中使用它们并没有错。
        【解决方案4】:

        在许多类型的数据中,使用 MovePrevious 是没有意义的。例如,如果您从网络连接接收数据,则提供 MovePrevious 将需要缓冲整个流,以防您调用该方法。这会浪费大量内存。

        话虽如此,拥有一个支持 MoveNext 和 MovePrevious 的 不同 类型可能会很有用(例如,双向链表可以支持这一点)。

        【讨论】:

        • @MarkSimpson:我希望看到一个Skip 方法,以及一个指示它是否可以接受负值的属性。没有任何合理的IEnumerator 应该有任何困难实现跳过-向前-N-items 方法,并且对于基于树的集合,它可以允许从 N 的集合中检索 M 个项目需要 O(M+logN) 而不是 O (MlogN) 或 O(N),否则这是可以通过 IList&lt;T&gt; 实现的最好的。
        【解决方案5】:

        IEnumerable[&lt;T&gt;] 表示数据序列,而不是随机访问列表。并非所有序列都可以反转,甚至重播。基于网络流、数据库访问等的序列——或者这种美:

        IEnumerable<int> GetData() {
            Random rand = new Random();
            while(true) { yield return rand.Next(); }
        }
        

        您能做的最好的事情就是重新开始 - 不是调用Reset()(已弃用),而是获取一个新的枚举器。

        即使没有Random,想出无法反转的简单序列也是微不足道的(没有缓冲和反转缓冲区)。对于您想要的,请考虑查看IList[&lt;T&gt;] - 您可以通过索引器以任何顺序访问数据。

        【讨论】:

        • 谢谢 Marc,如果您正在缓冲也可能是另一个 IEnumerable,这可能是不是每个人都想要的开销,对吧?
        • 确实 - 如果您有一个大(或无限)序列,请不要调用缓冲操作(ReverseOrderByGroupBy);-p
        • 对于某些类型的集合(例如树或跳过列表),可搜索的枚举比IList&lt;T&gt; 更有效。鉴于IEnumerator 的任何合理实现都可以将SkipAhead(int N) 实现为N 调用MoveNext,但许多人可以通过更快的方式实现它,将其作为IEnumerator&lt;T&gt; 的一部分似乎是明智的。
        • @supercat “任何合理的” - 根据我的经验;很多枚举数的来源是不可寻找的
        • @MarcGravell:任何合理的枚举器都可以通过简单地调用自己的MoveNext 方法n 次来跳过n 项,就像消费者一样有效。我会假设有足够多的情况,消费者只会对可枚举序列的一部分感兴趣,并且足够多的情况,知道代码的枚举器不会对许多项目感兴趣,可以避免完全处理它们的工作,让消费者让调查员知道何时不需要第一个 n 项目通常是一种胜利。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多