源码位置:
注意事项:
version:每当集合(List / Dictionary)中发生添加、删除等变化时,版本(version)会被增加。在MoveNext()方法中先检查版本是否与遍历开始时的版本一致,如果不一致,则说明在遍历过程中集合有了变化,抛出异常。
Dispose():Enumerator类的Dispose()方法为空实现,因为使用List和Dictionary的枚举器都不需要进行资源的释放。
List和Dictionary以及枚举器的基本概念解析可参考之前的文章。
源码
List中实现枚举器的Enumerator结构体的部分源代码:
public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
{
private List<T> list;
private int index;
private int version;
private T current;
public Enumerator(List<T> list)
{
this.list = list;
index = 0;
version = list._version;
current = default(T);
}
public bool MoveNext()
{
List<T> localList = list;
if (version == localList._version && ((uint)index < (uint)localList._size))
{
current = localList._items[index];
index++;
return true;
}
return MoveNextRare();
}
private bool MoveNextRare()
{
if (version != list._version)
{
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
}
index = list._size + 1;
current = default(T);
return false;
}
public T Current
{
get { return current; }
}
object System.Collections.IEnumerator.Current
{
get
{
if (index == 0 || index == list._size + 1)
{
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
}
return Current;
}
}
void System.Collections.IEnumerator.Reset()
{
if (version != list._version)
{
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
}
index = 0;
current = default(T);
}
public void Dispose()
{
}
}
从上述代码中可以看出,List的Enumerator是一个结构体,实现了IEnumerator和System.Collections.IEnumerator接口,因此同时支持泛型和非泛型方式枚举List集合。
成员变量:List的引用、当前索引位置、List实例的版本号和当前项的值等。
在MoveNext()方法中,先判断List实例的版本号是否发生变化,如果没有,则将当前项设置为当前索引位置对应的元素,并将索引位置加1。如果版本号发生变化或者索引越界,则调用MoveNextRare()方法抛出异常。在Reset()方法中,如果版本号发生变化,则抛出异常。
C# 外部调用 List 枚举器的方法:
class Program
{
static void Main(string[] args)
{
List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
// 使用foreach循环遍历List中的元素
foreach (int number in numbers)
{
Console.WriteLine(number);
}
// 使用枚举器手动遍历List中的元素
IEnumerator<int> enumerator = numbers.GetEnumerator();
while (enumerator.MoveNext())
{
int number = enumerator.Current;
Console.WriteLine(number);
}
}
}
Dictionary中实现枚举器的Enumerator结构体的部分源代码:
public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, System.Collections.IEnumerator
{
private Dictionary<TKey, TValue> dictionary;
private int version;
private int index;
private KeyValuePair<TKey, TValue> current;
internal int getEnumeratorRetType; //Enumerator.Current属性应该返回键值对还是值。下面省略了实现。
internal Enumerator(Dictionary<TKey, TValue> dictionary, int getEnumeratorRetType)
{
this.dictionary = dictionary;
version = dictionary.version;
index = 0;
current = default(KeyValuePair<TKey, TValue>);
this.getEnumeratorRetType = getEnumeratorRetType;
}
public bool MoveNext()
{
// 如果字典版本发生了变化,抛出异常
if (version != dictionary.version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
while ((uint)index < (uint)dictionary.count)
{
if (dictionary.entries[index].hashCode >= 0)
{
current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
index++;
return true;
}
index++;
}
index = dictionary.count + 1;
current = default(KeyValuePair<TKey, TValue>);
return false;
}
public KeyValuePair<TKey, TValue> Current
{
get { return current; }
}
void System.Collections.IEnumerator.Reset()
{
if (version != dictionary.version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = 0;
current = default(KeyValuePair<TKey, TValue>);
}
object System.Collections.IEnumerator.Current
{
get { return current; }
}
void IDisposable.Dispose()
{
}
}
从上述代码中可以看出,Dictionary的Enumerator也是一个结构体,实现了IEnumerator>和System.Collections.IEnumerator接口,同时支持泛型和非泛型方式枚举Dictionary集合。
成员变量:Dictionary的引用、Dictionary实例的版本号、当前索引位置、当前键值对值等。
当GetEnumerator()方法被调用时,会创建一个Enumerator对象,其中的index变量被初始化为0,current变量被初始化为默认的KeyValuePair值。MoveNext()方法在每次迭代时被调用,当循环遍历到一个存在的键值对时,会创建一个新的KeyValuePair对象,然后返回true。如果整个Dictionary中的键值对已经被遍历完,则返回false。Current属性表示当前指向的键值对。Reset()方法将index设置为0,current设置为默认值,用于重新开始遍历,在Reset()中,如果Dictionary实例的版本号发生变化,则抛出异常。
C# 外部调用 Dictionary 枚举器的方法:
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("name", "Tom");
dict.Add("age", "18");
dict.Add("gender", "male");
// 使用foreach 遍历 Dictionary 中的键-值对
foreach (KeyValuePair<string, string> pair in dict)
{
Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
}
// 使用枚举器遍历 Dictionary 中的键-值对
IEnumerator<KeyValuePair<string, string>> enumerator = dict.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine("{0}: {1}", enumerator.Current.Key, enumerator.Current.Value);
}
}
}