【问题标题】:When should I separately implement IEnumerator<T>?什么时候应该单独实现 IEnumerator<T>?
【发布时间】:2014-06-10 13:41:34
【问题描述】:

在集合的框架类中,我经常看到IEnumerator&lt;T&gt;单独实现为一个内部类,并在GetEnumerator方法中返回它的一个实例。

现在假设我正在编写自己的集合类,它将有一个像 List&lt;T&gt;T[] 这样的内置集合在内部充当持有者,这样说:

public class SpecialCollection<T> : IEnumerable<T>
{
    List<T> list;

    public SpecialCollection<T>()
    {

    }

    public IEnumerator<T> GetEnumerator()
    {
        return list.GetEnumerator();

        //or
        return list.Where(x => some logic).GetEnumerator(); 

        //or directly rely on yield keyword
        yield return x; //etc
    }
}

我应该编写自己的枚举器类,还是可以返回List&lt;T&gt; 类的枚举器?在什么情况下我应该编写自己的枚举器类?


我也有一个相关的问题。如果它不是那么重要或没有太大区别,为什么 BCL 中的每个集合类都编写自己的 IEnumerator

例如,List&lt;T&gt; 类有类似的东西

T[] items;
public IEnumerator<T> GetEnumerator()
{
    return new List<T>.Enumerator(items);
}

【问题讨论】:

  • 相对于什么?其中许多类是在迭代器出现之前编写的。
  • 泛型是与 yield 关键字同时引入的,如果我没记错的话
  • @JeppeStigNielsen 我认为// some logic 的意思是可以向枚举器添加一些额外的逻辑。
  • @Maarten 假设我编写自己的类型class MyColl : IEnumerable&lt;int&gt;。我可以在内部将我的值保存在一个数组中,所以有一个字段private int[] array;。现在,当我需要实现通用接口时,我不能只说return array.GetEnumerator();(编译时错误),因为数组的非通用性质(这里是int[])。我可以通过return array.Select(x =&gt; x).GetEnumerator(); 解决这个问题。但这很浪费。而是使用return ((IEnumerable&lt;int&gt;)array).GetEnumerator(); 或等效的return array.AsEnumerable().GetEnumerator();。明白我的意思了吗?
  • @nawfal:如果您返回数组的IEnumerator&lt;T&gt;,您将无法检测到调用之间的列表更改,这应该会触发异常。

标签: c# .net enumerator ienumerator design-guidelines


【解决方案1】:

第一个问题的答案是:当收益率不能满足您的需求时。

第二个问题的答案是:这些频繁使用的类型具有异常严格的性能要求,因此枚举器是自定义构建的。我最近写了一些关于这个的文章;见:

http://ericlippert.com/2014/05/21/enumerator-advance/

http://ericlippert.com/2014/06/04/enumerator-bounds/

【讨论】:

    【解决方案2】:

    只回答一部分:

    List&lt;T&gt; 有自己的枚举器实现有两个原因:

    • 它不能只返回其支持数组的迭代器,因为:
      • 它需要检测列表中的结构变化(添加和删除)以使枚举器无效
      • 数组可能比列表大。 (使用Take 可以解决这个问题,但代价是另一个间接级别)
    • 上面可以使用迭代器块执行(尽管首先必须有一个非迭代器方法,以便在调用时而不是第一次迭代时捕获列表的“结构版本”),但是与实际高度优化的可变结构实现相比,效率相对较低

    在这里使用可变结构has certain issues,但是当以预期方式使用时,它避免了堆分配、通过引用调用虚拟方法等。

    【讨论】:

    • 数组长度是另一个极好的点,很容易错过。但我确实认为从框架中返回内置类型的枚举器,如字典、列表、堆栈、队列、集合等(当然数组除外)是可以的,考虑到它们的枚举器处理这个集合修改问题。不是吗?
    • @nawfal:在什么情况下返回枚举数?这通常没关系,但有时不是。这里没有万能的答案。
    • 我的意思与我在问题中提供的示例相同。如果我的类SpecialCollection&lt;T&gt; 的支持集合类型是List&lt;T&gt;,那么从SpecialCollection&lt;T&gt;.GetEnumerator 方法返回List&lt;T&gt;.GetEnumerator 是没有错误的,我猜。
    • @nawfal:是的,如果您没有任何其他特殊要求,我认为应该没问题。
    • 嗯,我明白了。谢谢。
    猜你喜欢
    • 2014-01-25
    • 2020-06-03
    • 2010-10-02
    • 1970-01-01
    • 2011-10-14
    • 2013-02-25
    • 2013-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多