在集合的框架类中,我经常看到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);
}
你的第一个问题的答案是:当收益率不能满足你的需求时。
第二个问题的答案是:这些大量使用的类型具有异常严格的性能要求,因此枚举器是自定义构建的。我最近写了一些关于这个的文章;看到:
http://ericlippert.com/2014/05/21/enumerator-advance/http://ericlippert.com/2014/06/04/enumerator-bounds/只回答一个问题:
List<T>
有自己的枚举器实现有两个原因:
- 它不能只返回它的后端数组的迭代器,因为:
- 它需要检测列表中的结构变化(添加和删除),以便使枚举符 无效。
- 数组可能比列表大。(使用
Take
可以解决这个问题,但代价是另一个间接级别)
- 上面的操作可以用迭代器块来执行(尽管首先必须有一个非迭代器方法,以便在调用时而不是第一次迭代时捕获列表的"结构版本"),但是与实际的高度优化的可变结构体实现相比,这是相对低效的
在这里使用可变结构有一定的问题,但是当以预期的方式使用时,它避免了堆分配,通过引用调用虚拟方法等。