1、首先看一个简单的列子
1 int[] myArray = { 1, 32, 43, 343 };
2 //很少这样写的
3 IEnumerator myie = myArray.GetEnumerator(); //获取需要遍历的枚举数
4 myie.Reset(); //重置
5 while (myie.MoveNext())
6 {
7 int i = (int)myie.Current; //这边涉及一个装箱的操作
8 Console.WriteLine("Value: {0}", i);
9 }
10 //一般情况下是这样写的
11 foreach(int item in myArray )
12 {
13 Console.WriteLine(item.ToString());
14 }
在大部分的情况下,很多人会使用for和foreach来遍历数组,而对于上面的语法却用的很少,但是对foreach的具体来历还很模糊!
2、先从foreach说起
大家都知道要实现foreach的必须要实现IEnumerable和IEnumerator的接口,只有实现了它们,才能实现遍历,所以要讲foreach的来历,必须要把那两个接口给搞清楚点!
这边也想说明一点的是:如果对这两个接口有了一定的了解后,只要实现那个GetEnumerator方法即可,而不需要实现于IEnumerable接口,这只是针对对这两个接口有一定了解的朋友!
3、IEnumerable
具体的作用:就是使实现这个接口的对象成为可枚举类型。
IEnumerable接口包含一个GetEnumerator方法,返回值为IEnumerator的类型!
代码如下:
1 public class MyColors : IEnumerable
2 {
3 string[] colors = { "Red", "Yellow", "Biue" };
4
5 public IEnumerator GetEnumerator()
6 {
7
8 return colors.GetEnumerator();
9
10 }
11 }
那么我们在客户端进行调用的时候就可以这样做了!
1 MyColors colors = new MyColors();
2 foreach (string item in colors)
3 {
4 Console.WriteLine(item);
5 }
这样就能使用实现这个接口的对象进行foreach遍历了,就是这么简单的一个列子,这边可能有的人会有疑问,我们就实现了IEnumerable接口但却没实现IEnumerator接口,可是我们不是说只有实现了这两个接口才可以进行foreach遍历吗?可以这样说当使用forach进行遍历的时候,编译器会到这个对象的类中去寻找GetEnumerator方法,找到这个方法后返回这个一个IEnumerator的对象(枚举数),而我这边返回的是“colors.GetEnumerator()”,是一个数组对象调用它本身中的“GetEnumerator”方法,所以说数组本身就实现了IEnumerator接口,那么两个接口都实现了,不就好实现foreach遍历了,其实在实现遍历枚举数的时候编译器会自动去调用数组中实现枚举数的类中的方法(这将在下一篇中讲到)!
4、IEnumerator
接口的作用:实现可枚举数
首先看一下接口的定义:
包含一个属性两个方法
MoveNext → 把当前的项移动到下一项(类似于索引值),返回一个bool值,这个bool值用来检查当前项是否超出了枚举数的范围!
Current → 获取当前项的值,返回一个object的类型(这边会涉及到装箱和拆箱的问题 → 后面讲泛型的时候会涉及到)!
Reset → 顾名思义也就是把一些值恢复为默认值,比如把当前项恢复到默认状态值!
代码如下:
1 public class MyIEnumerator : IEnumerator
2 {
3
4 string[] colors; //定义一个数组,用来存储数据
5 int position = -1; //定义当前项的默认值,也就是索引值,一开始认识数组的索引从“0”开始,怎么默认设置他为“-1”呢,最后才想明白,这样设置是合情合理的!
6
7 public MyIEnumerator(string[] colors)
8 {
9 this.colors = new string[colors.Length];
10
11 for (int i = 0; i < this.colors.Length; i++)
12 {
13 this.colors[i] = colors[i];
14 }
15 }
16
17 public object Current //根据当前项获取相应的值
18 {
19 get
20 {
21 return colors[position]; //返回当前项的值,但是会做一个装箱的操作!
22 }
23 }
24
25 public bool MoveNext() //移动到下一项
26 {
27 if (position < colors.Length - 1) //这就是设置默认值为-1的根据
28 {
29 position++;
30 return true;
31 }
32 else
33 {
34 return false;
35 }
36 }
37
38 public void Reset() //重置当前项的值,恢复为默认值
39 {
40 this.position = -1;
41 }
42 }
在第三点讲到的IEnumerable接口中GetEnumerator方法是获取要遍历的枚举数,在我们没有创建自己的遍历枚举数的类时,我们使用的是Array的遍历枚举数的方法(关于数组的可枚举类型和枚举数会在下一篇讲到),但这个有的时候不一定适合我们,我们需要为自己定制一个更合适的,所以我们要创建自己的枚举数类(也就是上面的代码),把第三点和第四点的代码合并起来(改动一点代码),如下:
1 public class MyColors //: IEnumerable
2 {
3 string[] colors = { "Red", "Yellow", "Biue" };
4
5 public IEnumerator GetEnumerator()
6 {
7 return new MyIEnumerator(colors);
8
9 //这边就是我们要改动的地方,返回一个自己创建的类型,不是在返回数组的IEnumerator对象了,而是我们自己创建的枚举数对象→MyIEnumerator
10 }
11 }
客户端是不要改动代码的,然后只要使用foreach直接遍历就可以了!
5、关于可枚举类型和枚举数
①可枚举类型 → 实现IEnumerable接口,可以不需要直接实现这个接口,但必须有个GetEnumerator方法,返回值类型必须为IEnumerator类型,也就是第四点最后一段代码中接口注释的那种写法!
②枚举数 → 实现IEnumerator接口,实现全部方法,首先是调用GetEnumerator返回一个类型为IEnumerator的枚举数,然后编译器会隐式的调用实现IEnumerator类中的方法和属性!
总结:所以实现foreach遍历,必须达到上面的两种条件才能进行遍历对象,他们可以写在一起也可以分开,最好是分开,进行职责分离,一个类干一件事总归是好事!
6、图示,引用书中的图片
希望园子里面的朋友能过多多指教!
来源:http://www.cnblogs.com/yangcaogui/archive/2011/12/04/2266589.html