# 枚举器
如果某个类没有实现接口 IEnumerable,当对这个类的对象使用 foreach 时,我们就会得到一个错误提示:
编译器错误 CS1579
若要使用 foreach 语句循环访问某个集合,该集合必须满足以下要求:
- 它必须是一个接口、类或结构。
- 它必须包含一个返回类型的公共 GetEnumerator 方法。
- 返回类型必须包含一个名为 Current 的公共属性和一个名为 MoveNext 的公共方法。
查找官方文档后发现:
- GetEnumerator 是在 IEnumerable 中定义的
- Current 和 MoveNext 是在 IEnumerator 中定义的
# 枚举器接口
- IEnumerable:可枚举,使类型支持简单迭代(foreach)
- IEnumerator:枚举器,支持 MoveNext (),可以自己控制迭代的节奏
# 枚举器接口的使用
public class Test : IEnumerable | |
{ | |
public int[] TestArray { get; set; } | |
public Test(int arrayLength) | |
{ | |
TestArray = new int[arrayLength]; | |
} | |
// 获取枚举器 传统的代码 | |
//public IEnumerator GetEnumerator() | |
//{ | |
// return new TestEnumerator() { Target = TestArray }; | |
//} | |
// 获取枚举器 新式的代码 | |
public IEnumerator GetEnumerator() | |
{ | |
/* yield 的内部 | |
* 将 yield 之前的所有代码分配到 MoveNext () 中 | |
* 将 return 后面的数据分配到 Current 中 | |
*/ | |
/* 代码的执行过程 | |
* 调用 MoveNext () 时执行 | |
* 执行到 yield 时(return AllObject [i] 会执行),暂时离开 | |
* 再次调用 MoveNext () 时继续执行 | |
*/ | |
for (int i = 0; i < TestArray.Length; i++) | |
{ | |
yield return TestArray[i]; | |
} | |
} | |
} | |
/// <summary> | |
/// 迭代器 | |
/// </summary> | |
public class TestEnumerator : IEnumerator | |
{ | |
public int[] Target; | |
private int Index = -1; | |
// 获取当前数据 | |
object IEnumerator.Current | |
{ | |
get | |
{ | |
if (Index >= Target.Length) | |
{ | |
Index = 0; | |
} | |
return Target[Index]; | |
} | |
} | |
public bool MoveNext() | |
{ | |
Index++; | |
return Index < Target.Length; | |
} | |
public void Reset() | |
{ | |
Index = -1; | |
} | |
} |
# foreach 的原理
// 以上文中创建的类为基础 | |
private static void Main() | |
{ | |
Test test = new Test(5); | |
for (int i = 0; i < test.TestArray.Length; i++) | |
{ | |
test.TestArray[i] = i; | |
} | |
//foreach 的原理 | |
//1. 获取迭代器: | |
IEnumerator temp = test.GetEnumerator(); | |
//2. 移动到下一个元素 | |
while (temp.MoveNext()) | |
{ | |
//3. 获取元素 | |
Console.WriteLine(temp.Current); | |
} | |
} |