C# foreach 底层原理分析及自定义 MyList

 foreach 在我们进行.net 开发时算是比较常见的,我们以List为例,调试一下源码。

了解foreach内部调用机制,理解一个类需要满足什么条件才能使用foreach。

//List定义定义 
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
//4种接口定义
IEnumerable
    IEnumerator GetEnumerator();    
IEnumerator
    bool MoveNext();
        object Current;
    void Reset();

IEnumerable<out T> : IEnumerable
    IEnumerator<T> GetEnumerator();
IEnumerator<out T> : IDisposable, IEnumerator
    T Current;

0- 新建控制台程序:

    static void Main(string[] args)
    {

        List<int> test = new List<int> { 1, 2, 3, 4};
        foreach (var item in test)
        {
            Console.WriteLine();
        }

        Console.ReadKey();
    }

1- List进行了初始化,将一个空数组赋值给了_items数组

 

2- 然后List进行初始化的时候调用了Add方法,然后进行了扩容判断 (具体如何扩容就不贴出来了)

 3- 执行foreach的时候,调用了GetEnumerator() ,获得了一个迭代器。注意看这里的返回值是一个List<T>.Enumerator,它在List内部被定义为了一个结构体。

 

 3-1 遍历数据的时候 第一步执行 迭代器的 MoveNext方法,

3-2 获取迭代器 Current 属性值

 

这里我们得出一个结论,只要一个类实现了 IEnumerable 接口能够返回一个迭代器,那就可以使用foreach进行遍历。

举一反三,我们理解了foreach原理后,模仿List 创建一个我们自己的MyList,然后可以传递Func委托过滤遍历的类(玩一玩,运用一下)

    public class MyList<T> : IEnumerable
    {
        private static readonly T[] _emptyArray = new T[0];
        private const int _defaultCapacity = 4;
        private T[] _items;
        private int _size;
        private int _version;
        private Func<T, bool> _filterFunc;

        public int Count => _size;
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public MyList()
        {
            this._items = _emptyArray;
        }

        /// <param name="func"></param>
        public MyList(Func<T, bool> func)
        {
            this._items = _emptyArray;
            _filterFunc = func;
        }

        public IEnumerator GetEnumerator()
        {
            return new Enumerator(this);
        }

        public void Add(T item)
        {
            if (this._size == this._items.Length)
                this.EnsureCapacity(this._size + 1);
            this._items[this._size++] = item;
            ++this._version;
        }

        /// <summary>
        /// 扩容机制
        /// </summary>
        /// <param name="min"></param>
        private void EnsureCapacity(int min)
        {
            if (this._items.Length >= min)
                return;
            int num = this._items.Length == 0 ? 4 : this._items.Length * 2;
            if ((uint)num > 2146435071U)
                num = 2146435071;
            if (num < min)
                num = min;
            this.Capacity = num;
        }

        public int Capacity
        {
            get => this._items.Length;

            set //有点子监听的感觉
            {
                if (value == this._items.Length)
                    return;
                if (value > 0)
                {
                    T[] objArray = new T[value];
                    if (this._size > 0) //扩容真正实现在这里
                        Array.Copy((Array)this._items, 0, (Array)objArray, 0, this._size);
                    this._items = objArray;
                }
                else
                    this._items = _emptyArray;
            }
        }

        /// <summary>
        /// 迭代器定义
        /// </summary>
        public struct Enumerator : IEnumerator
        {
            private MyList<T> _list;
            private int _index;
            private int _version;
            private T current;

            internal Enumerator(MyList<T> list)
            {
                this._list = list;
                this._index = 0;
                this._version = list._version;
                this.current = default(T);
            }

            public bool MoveNext()
            {
                if (this._version != _list._version || (uint)this._index >= (uint)_list._size)
                    return this.MoveNextRare();

                this.current = _list._items[this._index];
                ++this._index;

                //当不满足过滤条件时,foreach遍历Current返回为空
                if (_list._filterFunc!=null && !_list._filterFunc(this.current))
                    this.current = default(T);

                return true;
            }

            private bool MoveNextRare()
            {
                this._index = this._list._size + 1;
                this.current = default(T);
                return false;
            }

            public object Current => this.current;

            public void Reset()
            {
                _index = 0;
                current = default(T);
            }
        }
    }

控制台测试程序:

    class Program
    {
        static void Main(string[] args)
        {
            MyList<int> list1 = new MyList<int>(new Func<int, bool>(m => m > 5)) {1, 6, 3, 9, 10, 2, 4};
            foreach (var item in list1)
            {
                if (item != null)
                    Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }

输出结果:(初始化MyList时 我们用了int?,  因为default<int> 默认值为0,对于后面的null判空不生效)

 最后补充一点,迭代器 MoveNext()方法返回值false时,会直接终止遍历。

原文地址:https://www.cnblogs.com/stephenzengx/p/13998427.html