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时,会直接终止遍历。