什么时候我应该单独实现IEnumerator

本文关键字:IEnumerator 实现 我应该 单独 什么时候 | 更新日期: 2023-09-27 18:07:51

在集合的框架类中,我经常看到IEnumerator<T>作为内部类单独实现,并且它的实例在GetEnumerator方法中返回。

现在假设我正在编写我自己的集合类,它将有一个内置的集合,如List<T>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<T>类的枚举器?在什么情况下我应该编写自己的枚举器类?


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

例如,List<T>类有类似

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

什么时候我应该单独实现IEnumerator<T>

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

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

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

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

只回答一个问题:

List<T>有自己的枚举器实现有两个原因:

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

在这里使用可变结构有一定的问题,但是当以预期的方式使用时,它避免了堆分配,通过引用调用虚拟方法等。