请解释 C# 中 IEnumerable 与 IEnumerator 接口在职责和用法上的差异。
考察说明
考查对 C# 集合遍历核心接口模型的理解,以及二者协作而非对立的本质。
回答思路
- 【回答框架 1】IEnumerable 与 IEnumerator 是 C# 中实现集合遍历的一对协作接口。IEnumerable 的核心职责是定义一个可被迭代的集合,它只提供一个 GetEnumerator 方法,用于返回一个与当前集合关联的枚举器对象。而 IEnumerator 负责实际的迭代状态管理,它包含 Current 属性以及 MoveNext 和 Reset 方法,通过 MoveNext 推进游标并返回是否还有元素,Current 获取当前元素。
- 【回答框架 2】在用法上,foreach 语句的底层机制正是调用集合的 GetEnumerator 获得一个 IEnumerator,然后循环调用 MoveNext 并读取 Current,直到 MoveNext 返回 false。因此,IEnumerable 是集合面向使用者的门面,而 IEnumerator 则是迭代过程的执行者,二者通常联合使用,而不是彼此替代。
- 【回答框架 3】从设计角度看,IEnumerable 定义的是可枚举的抽象能力,IEnumerator 定义的是具体的枚举状态机。实现 IEnumerable 的类可以通过 yield return 语法让编译器自动生成一个 IEnumerator,从而简化自定义集合的迭代实现。这种分离使得同一个集合可以产生多个独立的枚举器,各枚举器维护自己的遍历位置,互不干扰。
- 【回答框架 4】实践中,遇到需要遍历集合的接口参数时通常使用 IEnumerable,而如果需要手动控制迭代过程或直接操作游标,则使用 IEnumerator。理解二者区别有助于正确设计集合类与迭代逻辑,也能避免混淆接口的职责边界。
- 【关键点 1】IEnumerable 提供 GetEnumerator 方法,声明集合可被遍历;IEnumerator 提供 Current、MoveNext 和 Reset,负责具体的遍历状态。
- 【关键点 2】foreach 的内部实现依赖 IEnumerator:先获取枚举器,再循环 MoveNext 与 Current。
- 【关键点 3】使用 yield return 可让编译器自动生成 IEnumerator,简化自定义迭代。
- 【关键点 4】同一集合上的多个枚举器相互独立,各自维护位置。
- 【关键点 5】接口协作关系:IEnumerable 面向集合消费者,IEnumerator 面向迭代执行者。
- 【易错点 1】不要将 IEnumerable 与 IEnumerator 视为功能对等的可选项,二者是职责分离的协作关系。
- 【易错点 2】Reset 方法在多数枚举器中可能不受支持或抛异常,不应依赖它重置遍历。
- 【易错点 3】实现 IEnumerable 时若手动编写 IEnumerator,需注意正确实现 MoveNext 和 Current 的状态推进,否则可能导致死循环或空遍历。