.net基础---迭代器
迭代器
迭代器是一种快速访问集合元素的方法,它通过封装记录当前访问到的集合元素的下标值(position),访问当前元素直接获取其中的当前值即可(当前值通过下标获取);访问下一元素时通过判断是否存在下一个元素,如果存在则通过moveNext方式,将当前访问的下标值往后移(+1)。
在.net中迭代器的实现主要就是foreach语法。能够使用foreach的语法的必须实现IEnumerable接口。IEnumerable定义GetEnumerator接口返回的IEnumerator,而IEmerator接口则是迭代的真正实现。
示例代码
1 /// <summary> 2 /// 可迭代类(集合,例如List) 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class CustomerEnumable<T> : IEnumerable<T> 6 { 7 8 private T[] _data; 9 #region 集合本来的属性 10 public int Count => _data.Length; 11 /// <summary> 12 /// 索引器 13 /// </summary> 14 /// <param name="index"></param> 15 /// <returns></returns> 16 public T this[int index] 17 { 18 get { return _data[index]; } 19 set { _data[index] = value; } 20 } 21 //........ 22 23 public CustomerEnumable(T[] data) 24 { 25 this._data = data; 26 } 27 #endregion 28 /// <summary> 29 /// 获取非泛型迭代器 30 /// </summary> 31 /// <returns></returns> 32 public IEnumerator GetEnumerator() 33 { 34 return new CustomerEnumerator<T>(_data); 35 } 36 /// <summary> 37 /// 获取泛型迭代器 38 /// </summary> 39 /// <returns></returns> 40 IEnumerator<T> IEnumerable<T>.GetEnumerator() 41 { 42 return new CustomerEnumerator<T>(_data); 43 } 44 } 45 /// <summary> 46 /// CustomerEnumable的迭代器实现 47 /// </summary> 48 /// <typeparam name="T"></typeparam> 49 public class CustomerEnumerator<T> : IEnumerator<T> 50 { 51 private int _position = -1; 52 private T[] _data; 53 /// <summary> 54 /// 当前值 55 /// </summary> 56 public object Current => _data[_position]; 57 /// <summary> 58 /// 当前值(泛型) 59 /// </summary> 60 T IEnumerator<T>.Current => _data[_position]; 61 62 public CustomerEnumerator(T[] data ) 63 { 64 this._data = data; 65 } 66 public void Dispose() 67 { 68 this._data = null; 69 } 70 /// <summary> 71 /// 判断下一个元素是否存在,存在则将position加1,并返回true,否则返回false表示无下一个元素 72 /// </summary> 73 /// <returns></returns> 74 public bool MoveNext() 75 { 76 if (_position < _data.Length - 1) 77 { 78 _position++; 79 return true; 80 } 81 else 82 { 83 return false; 84 } 85 } 86 /// <summary> 87 /// 重设 88 /// </summary> 89 public void Reset() 90 { 91 this._position = -1; 92 } 93 }
使用示例
1 string[] brands = new string[] { "Red b", "Green g", "Yellow d", "Blue Z" }; 2 var brandList = new CustomerEnumable<string>(brands); 3 4 5 //1、使用for遍历 6 for (int i = 0; i < brandList.Count; i++) 7 { 8 Console.WriteLine(brandList[i]); 9 } 10 //2、使用foreach语法 11 foreach (var brandItem in brandList) 12 { 13 Console.WriteLine(brandItem); 14 } 15 //3、直接通过迭代器访问(其实就是foreach的实际实现) 16 var brandEnumerator = brandList.GetEnumerator();//因为GetEnumerator方法每次都是返回新的迭代实现,所以无需手工调用Reset 17 while (brandEnumerator.MoveNext()) 18 { 19 Console.WriteLine(brandEnumerator.Current); 20 }
附:
1、foreach遍历集合比直接使用for要快?
这个是不一定的,因为通过了解迭代器的实现,我们可以知道它最终还是按照下标去顺序遍历的。而这些集合基本上都是内存连续的。当我们使用for也是按照下标顺序获取(不存在跳跃的情况下),其实性能是差不多的。
当然foreach语法比较简洁,不用我们去维护下标值,所以还是建议使用foreach的。
2、.net中yield关键字其实是一种语法糖,最终还是通过实现IEnumberable<T>、IEnumberable、IEnumberator<T>和IEnumberator接口实现的迭代功能(可以通过IL码查看其内部实现,此处略)。
简单示例
1 public IEnumerable<string> GetSubjectEnumerable() 2 { 3 yield return "C#"; 4 yield return "Java"; 5 yield return "Python"; 6 yield return "Go"; 7 }
1 foreach (var item in GetSubjectEnumerable()) 2 { 3 Console.WriteLine(item); 4 }
---------
以上就是对迭代器概念的回顾