引言
最近翻看了之前的学习笔记,看到foreach,记得当时老师讲的时候,有点犯浑,不是很明白,这好比,上小学时,你不会乘法口诀,但是随着时间的增长,你不自觉的都会了,也悟出个小道理,有些东西,你当时不太懂,但随着你的阅历和经验的增长,有那么一天你会恍然大悟,哦,原来是这样。
自定义集合类
提到foreach就不得不说集合,那么就先从自定义的集合开始吧。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Wolfy.自定义集合 8 { 9 /// <summary> 10 /// 自定义集合类 11 /// </summary> 12 public class MyArrayList 13 { 14 /// <summary> 15 /// 集合的容量属性 成倍数增加0,4,8,且只读 16 /// </summary> 17 public int Capacity 18 { 19 get 20 { 21 return this.objArray.Length == 0 ? 0 : this.index > this.objArray.Length ? this.objArray.Length * 2 : this.objArray.Length; 22 } 23 } 24 /// <summary> 25 /// 集合实际元素个数 且只读 26 /// </summary> 27 public int Count 28 { 29 get 30 { 31 return this.index; 32 } 33 } 34 private int index; 35 private object[] objArray; 36 public MyArrayList() 37 { 38 index = 0; 39 //初始化0长度的数组 40 this.objArray = new object[0]; 41 } 42 /// <summary> 43 /// 添加元素 44 /// </summary> 45 /// <param name="value">元素值</param> 46 public void Add(object value) 47 { 48 if (index >= this.objArray.Length) 49 { 50 object[] newArray = index == 0 ? new object[this.objArray.Length + 4] : new object[this.objArray.Length * 2]; 51 objArray.CopyTo(newArray, 0); 52 objArray = newArray; 53 objArray[index++] = value; 54 } 55 else 56 { 57 objArray[index++] = value; 58 } 59 } 60 /// <summary> 61 /// 添加数组 62 /// </summary> 63 /// <param name="objs"></param> 64 public void AddRange(object[] objs) 65 { 66 for (int i = 0; i < objs.Length; i++) 67 { 68 this.Add(objs[i]); 69 } 70 } 71 } 72 }
不知道自定义的集合和ArrayList是否一样,可以简单的测试一下。
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Wolfy.自定义集合 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 ArrayList list = new ArrayList(); 15 //MyArrayList list = new MyArrayList(); 16 Console.WriteLine("count:" + list.Count); 17 Console.WriteLine("Capacity:" + list.Capacity); 18 list.Add(1); 19 list.Add(1); 20 Console.WriteLine("count:" + list.Count); 21 Console.WriteLine("Capacity:" + list.Capacity); 22 list.Add(1); 23 list.Add(1); 24 list.Add(1); 25 Console.WriteLine("count:" + list.Count); 26 Console.WriteLine("Capacity:" + list.Capacity); 27 list.Add(1); 28 list.Add(1); 29 list.Add(1); 30 list.Add(1); 31 list.Add(1); 32 list.Add(1); 33 Console.WriteLine("count:" + list.Count); 34 Console.WriteLine("Capacity:" + list.Capacity); 35 object[] arr = { 1, 2, 3, 4, 5, 6 }; 36 list.AddRange(arr); 37 Console.WriteLine("count:" + list.Count); 38 Console.WriteLine("Capacity:" + list.Capacity); 39 Console.Read(); 40 } 41 } 42 }
此时是.Net中的ArrayList,结果:
自定义的集合,结果:
输出结果一样,那么现在用foreach遍历,自定义集合中的元素。F6编译,会提示错误。
foreach语句
其实foreach是怎样工作的呢?
众所周知foreach中in后面的对象应该是实现IEnumerable接口的,程序运行时本质是在调用IEnumerable的GetEnumerator函数来返回一个IEnumerator对象,foreach就是利用IEnumerator对象的Current,MoveNext和Reset成员来进行一段数据的枚举。简单的代码实现如下:
1 //System.Collections下的IEnumerator 2 IEnumerator enumerator = this.objArray.GetEnumerator(); 3 while (enumerator.MoveNext()) 4 { 5 Console.WriteLine(enumerator.Current); 6 }
将这个代码放在自定义集合中,定义一个方法GetArray(),然后测试一下
1 /// <summary> 2 /// 得到所有的元素 3 /// </summary> 4 public void GetArray() 5 { 6 //System.Collections下的IEnumerator 7 IEnumerator enumerator = this.objArray.GetEnumerator(); 8 while (enumerator.MoveNext()) 9 { 10 Console.Write(enumerator.Current+“,”); 11 } 12 }
测试结果:
你运行会发现多出很多逗号,原因是执行后,enumerator没有被Dispose掉,而继承IDisposable的迭代器(IEnumerator)在foreach结束后会被正确处理掉(调用Dispose方法)。
自定义集合实现IEnumerable接口
实现IEnumerable接口必须实现它里面的成员GetEnumerator()方法:
1 public IEnumerator GetEnumerator() 2 { 3 throw new NotImplementedException(); 4 }
该方法的返回值为实现了IEnumerator接口的类的对象。那么现在需要定义一个实现了该接口的类。
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Wolfy.自定义集合 9 { 10 public class MyEnumerator : IEnumerator 11 { 12 /// <summary> 13 /// 返回当前指针指向的元素的值 14 /// </summary> 15 public object Current 16 { 17 get { throw new NotImplementedException(); } 18 } 19 /// <summary> 20 /// 将指针向前移动1位,并判断当前位有没有元素.指针默认在-1位置 21 /// </summary> 22 /// <returns></returns> 23 public bool MoveNext() 24 { 25 throw new NotImplementedException(); 26 } 27 /// <summary> 28 /// 重置 29 /// </summary> 30 public void Reset() 31 { 32 throw new NotImplementedException(); 33 } 34 } 35 }
迭代需要数组参数,在构造函数中将自定义集合中的数组传进来,并且在MoveNext中需要判断指针是否移动到数组的末尾,那么需要数组的长度。
MyArrayList中GetEnumerator()方法实现
1 public IEnumerator GetEnumerator() 2 { 3 return new MyEnumerator(this.objArray, this.Count); 4 }
MyEnumerator类
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Wolfy.自定义集合 9 { 10 public class MyEnumerator : IEnumerator 11 { 12 /// <summary> 13 /// 指针的默认位置 14 /// </summary> 15 private int index = -1; 16 private object[] objArray; 17 /// <summary> 18 /// 数组长度 19 /// </summary> 20 private int count; 21 public MyEnumerator(object[] objArray, int count) 22 { 23 24 this.objArray = objArray; 25 this.count = count; 26 } 27 /// <summary> 28 /// 返回当前指针指向的元素的值 29 /// </summary> 30 public object Current 31 { 32 get { return this.objArray[index]; } 33 } 34 /// <summary> 35 /// 将指针向前移动1位,并判断当前位有没有元素.指针默认在-1位置 36 /// </summary> 37 /// <returns></returns> 38 public bool MoveNext() 39 { 40 index++;//指针首先向前移动一位 41 if (index < this.count) 42 { 43 return true; 44 } 45 else 46 { 47 return false; 48 } 49 } 50 /// <summary> 51 /// 重置 52 /// </summary> 53 public void Reset() 54 { 55 index = -1; 56 } 57 } 58 }
测试,foreach语句,然后编译不再报错,说明已经成功了,结果如下:
这次后边不会多出逗号,原因实现了迭代器接口而迭代器继承自IDisposable接口,最后调用了Dispose()方法
代码下载,请戳这里:http://pan.baidu.com/s/1pJsGyHt
总结
foreach遍历in后面的对象需实现IEnumerable接口。
迭代器概念可参考:http://msdn.microsoft.com/zh-cn/library/dscyy5s0(VS.80).aspx
东西比较基础,以上是个人理解,如理解有误,请指正,以免误人子弟。