IEnumerable和IEnumerator
时间: 2025-03-04 22:04:03 浏览: 26
### C# 中 `IEnumerable` 和 `IEnumerator` 的区别及使用场景
#### 定义与功能差异
`IEnumerable<T>` 接口定义在 `System.Collections.Generic` 命名空间中,仅有一个方法 `GetEnumerator()`,该方法返回一个 `IEnumerator<T>` 类型的对象,用于遍历集合中的元素[^1]。
```csharp
public interface IEnumerable<out T> : IEnumerable {
IEnumerator<T> GetEnumerator();
}
```
相比之下,`IEnumerator<T>` 是枚举器接口,提供了具体的方法来遍历集合:
- `MoveNext()` 方法使枚举数推进到下一个元素。
- `Reset()` 方法将枚举数重置为其初始位置之前的位置。
- `Current` 属性获取当前元素的值。
```csharp
public class MyEnumerator<T> : IEnumerator<T>
{
public bool MoveNext()
{
// Implementation to move to the next element.
}
public void Reset()
{
// Implementation to reset the enumerator.
}
public T Current => throw new NotImplementedException();
object IEnumerator.Current => Current;
}
```
#### 实现方式的不同
当实现自定义集合时,通常会创建两个类:一个是实现了 `IEnumerable<T>` 的集合类,另一个是实现了 `IEnumerator<T>` 的枚举器类。这种设计使得每次调用 `GetEnumerator()` 都能获得一个新的枚举实例,从而允许多个独立的迭代过程并存于同一个集合上[^3]。
#### 使用场景对比
- **`IEnumerable<T>`** 更适合表示可列举的数据源或容器对象。通过此接口提供的机制,客户端代码能够利用 `foreach` 循环轻松访问其中的所有项而不必关心内部存储结构细节[^4]。
- **`IEnumerator<T>` 则更侧重于控制具体的迭代行为**。例如,在某些情况下可能希望暂停、恢复甚至回滚某个特定位置上的读取操作,则可以直接操纵此类类型的变量来进行这些动作[^5]。
#### 示例代码展示两者的配合工作原理
下面是一个简单的例子展示了如何在一个列表类型里同时支持这两种模式:
```csharp
using System;
using System.Collections;
using System.Collections.Generic;
// 自定义集合类实现 IEnumerable<T>
public class MyArrayList<T> : IEnumerable<T>
{
private List<T> _items = new List<T>();
public IEnumerator<T> GetEnumerator()
{
return new MyEnumerator<T>(_items);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
// 枚举器类实现 IEnumerator<T>
public class MyEnumerator<T> : IEnumerator<T>
{
private readonly List<T> _list;
private int currentIndex = -1;
public MyEnumerator(List<T> list)
{
_list = list ?? throw new ArgumentNullException(nameof(list));
}
public bool MoveNext()
{
if (currentIndex < _list.Count - 1)
{
currentIndex++;
return true;
}
else
{
return false;
}
}
public void Reset()
{
currentIndex = -1;
}
public T Current => currentIndex >= 0 && currentIndex < _list.Count ? _list[currentIndex] : default(T);
object IEnumerator.Current => Current;
public void Dispose() {}
}
```
在这个例子中,每当客户请求新的枚举器时都会得到一个全新的 `MyEnumerator<T>` 对象,这允许不同的循环逻辑各自拥有自己独立的状态信息而互不干扰。
阅读全文
相关推荐



















