C# 从List和Dictionary出发的枚举器解析

源码位置:

list.cs (microsoft.com)

dictionary.cs (microsoft.com)

注意事项:

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);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_40695640/article/details/130355690