上一篇文章讲到了如何构建自己的可枚举类型和枚举数!
这一片将重点讲解我们经常使用的数组,首先抛出一个疑问,为什么定义的数组可以进行foreach遍历?带着这个问题我们来一探究竟!
1、Array的可枚举类型和枚举数的创建
①首先看一个简单的列子
1 string[] strArray = { "aa", "bb", "cc", "dd" };
2 foreach (string item in strArray)
3 {
4 Console.WriteLine(item);
5 }
根据上一篇的知识,要想实现foreach遍历,就必须要实现可枚举类型以及构建自己的枚举数(或者调用其它方法实现枚举数的遍历),也就是实现IEnumerable和IEnumerator两个接口!
在说明之前插一句话C#中几乎所有的关键字都是对应着.NET一个类(也就是所谓的映射),比如“int”关键字就是对应的“System.Int32”,那么数组对应的是哪个类呢?答案是“Array”,下面就对Array类做一个探讨!
②通过Reflector工具查看Array类
说实话以前感觉Reflector离我太远,一直搞不清楚它对我有多大的用处,不过现在却改变了看法,因为当你想深入研究时才会知道它的真正作用!
a)查看Array实现了哪些接口
通过Reflector很容易看到它实现了哪些接口以及方法,属性,字段的定义以及实现!
上面很好的说明了Array已经实现了IEnumerabler接口,说明它现在就是个可枚举类型了,那它也一定实现了GetEnumerator方法!
b)Array的GetEnumerator方法
还是通过Reflector来查看:
图示中用方框框起来的是这个方法三个重要的部分(返回枚举数),我们关注的应该是return后面返回的那个对象,到现在为止我们还不知道它是干什么用的,不过我们在心理应该有点感觉到了,这个类是不是跟返回的枚举数有关系呢?带着这个问题我们进一步探讨!
c)查看ArrayEnumerator
点开ArrayEnumerator,会很惊讶的发现这个类实现了IEnumerator接口并实现了它所有的方法,以及定义了一个数组(_indices),起始项(startIndex)以及一些遍历,其它的方法和属性可以自己通过Reflector工具进行查看和学习!
看到这儿,头脑里面有没有一种豁然开朗的感觉,它的形式跟我们在第一篇讲的内容基本相似,都符合两个特点:
I, 实现IEnumerable接口,实现GetEnumerator方法,返回IEnumerator类型的枚举数,使定义的类变为可枚举类型!
II,实现IEnumerator接口操作返回的枚举数对象(其实枚举数本质上就是数组或集合),以至于可以依次返回集合中项的类对象!
所以说为什么数组或集合可以进行foreach遍历,就是因为微软封装了实现IEnumerator和IEnumerable接口,我们一般看不到,基本的实现步骤和思想就跟上一篇差不多,只是它里面方法,属性中的代码更加的详细而已,有兴趣的可以通入Reflector深入了解!
2、最后通过一张图来展示在进行foreach的时候,编译器的执行步骤是怎么样的?
说明一下:上面Array在调用GetEnumerator方法时返回是的一个“ArrayEnumerator”类型的对象,但是这个方法的返回值类型是IEnumerator,又因为ArrayEnumerator实现了IEnumerator接口才可以这样操作(也就是子类的引用给了父类)!
一直对“子类的引用给父类”这句话中的“引用”感到很困惑。百思不能其解,不过在这段学习的过程中对它有了一些自己的见解!
上面提到的引用其实是存储在栈中的引用地址,这个地址指向的是这个对象存放在堆中的数据(这边涉及到一点栈和堆的知识),而“子类的引用给父类”它的含义只是在说明“引用地址”的所有权发生了改变,而真正的数据却没有改变(包括存放的位置)!
对于上图的解释就这么多!
希望园子里面的园友多多拍砖,本人新手,需要各位指点一二!
下一篇:迭代器学习之三:IEnumerable和IEnumerator的泛型结构