C#集合:枚举IEnumerable

IEnumerable 和 IEnumerator

  IEnumerator接口定义了以向前方式遍历集合的基本底层协议。
  声明如下:

public interface IEnumerator
{
    bool MoveNext();
    object Current { get; }
    void Reset();
}
  • MoveNext将当前元素向前移动到下一个位置,如果集合没有更多元素,那么它会返回false。
  • Current返回当前位置元素。在取得第一个元素之前 必须先调用MoveNext,即使空集合也支持该操作。
  • Reset作用就是当前位置移回起点,并允许再一次枚举集合。此方法一般并不建议使用,因为完全可以重新实例化一个枚举器。

  通常,集合本身并不实现枚举器,而是通过IEnumerable接口提供枚举器:

public interface IEnumerable: 
{
    IEnumerator GetEnumerator();
}

  通过GetEnumerator返回枚举器,可以灵活地将迭代逻辑转移到另一个类上。此外,多个消费者可以同时枚举同一个集合而不互相影响。IEnumerable可以看作IEnumerator的提供者,它是所有集合类需要实现的最基础接口。

  下面示例了基本用法:

string s = "Hello";
IEnumerator rator = s.GetEnumerator();
while (rator.MoveNext())
{
    char c = (char)rator.Current;
    Console.Write(c + ".");
}

  然而,我们很少采用这种方式直接调用枚举器的方法,因为C#提供了更快捷的语法:foreach语句。

foreach (char c in s)
{
    Console.Write(c + ".");
}

IEnumerable<T> 和 IEnumerator<T>

  IEnumerable 和 IEnumerator 总是和它们的泛型版本同时实现:

public interface IEnumerator<T> : IEnumerator, IDisposable
{
    T Current { get; }
}
public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

  这些接口通过定义一个类型化的CurrentGetEnumerator强化了静态类型安全性,避免了值类型元素装箱的额外开销,更加方便了。

  集合类的标准做法是公开提供IEnumerable<T>接口,并通过显式接口实现,而“隐藏”非泛型的IEnumerable接口。如果直接调用GetEnumerator(),则返回类型安全的泛型IEnumerator<T>。但是有时候这个规则也会由于向后兼容性而破坏,比如数组就必须返回非泛型的以避免破坏之前C#版本代码。

  IEnumerator<T>也同时继承了IDisposable,这样枚举器就可以在枚举结束后确保释放这些资源。

posted @ 2022-06-29 20:57  一纸年华  阅读(562)  评论(0编辑  收藏  举报