时间:2022-8-5来源:本站原创作者:佚名

作为开发人员我们经常会在程序中编写foreach语句实现对类型的遍历,但是并不是所有的类型都可以遍历,这个知识点是绝大部分开发成员所知晓的。但是类型可以被foreach遍历的依据是什么部分程序员并不清楚,下面我就通过举例的方式来具体讲解foreach原理。

在这里我们首先自定义一个类型Cat并遍历这个类型:

//定义Cat类型classCat{}//遍历CatclassProgram{staticvoidMain(string[]args){Catcat=newCat();foreach(varitemincat){//morecode}}}

我们运行上述代码后编译器会提示错误“Cat”不包含“GetEnumerator”的公共定义,因此foreach语句不能作用于“Cat”类型的变量,由此错误提示我们可以得知如果Cat类型可以被foreach遍历,那么Cat类就必须实现GetEnumerator方法。下面我们就在Cat类中加入GetEnumerator方法。

classCat{//加入GetEnumerator方法的实现publicobjectGetEnumerator(){returnnull;}}

我们再次运行代码,这时程序出现如下两个错误提示:

foreach要求“Cat.GetEnumerator()”的返回类型“object”必须具有适当的公共MoveNext方法和公共Current属性;object并不包含“MoveNext”的定义。根据上述错误提示我们可以推断出GetEnumerator方法的返回值必须要有MoveNext方法和Current属性。但是我们目前并不知道GetEnumerator方法的返回值类型和Current属性是否是只读的,这种情况我们该怎么办呢?此时我们可以查看已经支持foreach遍历的类型是怎么做的,下面的代码段展示了string类型是如何实现的(只列出了关键代码)。

//morecodepublicCharEnumeratorGetEnumerator();//morecodepubicsealedclassCharEnumerator:ICloneabe,IEnumeratorchar,IEnumerator,IDisposable{publiccharCurrent{get;}//morecodepublicboolMoveNext();//morecode}

根据上述代码段我们仿写如下:

classCat{publicCatEnumeratorGetEnumerator(){returnnewCatEnumerator();}}classCatEnumerator{publiccharCurrent{get;}publicboolMoveNext(){returntrue;}}

这时我们编译发现原来的错误已经消失了,程序编译通过了。但是不要以为到这里就完了,Cat类仅仅包含这些是没有任何意义的,这些内容只是为了让程序通过编译而已,在实际开发中我们遍历的对象是一个序列,那么我们现在就在Cat类中添加一个固定的序列:

classCat{string[]datas=newstring[]{波斯猫,狸花猫,无毛猫,虎斑猫};publicCatEnumeratorGetEnumerator(){returnnewCatEnumerator();}}

我们已经添加了数据对象,那么foreach是如何访问到这个数据的呢?这时我们可以将数据对象通过GetEnumerator方法作为迭代计数器对象(CatEnumerator)构造函数的参数传递进去,然后迭代计数器对象提供一个属性将这些数据存储起来。

classCat{string[]datas=newstring[]{波斯猫,狸花猫,无毛猫,虎斑猫};publicCatEnumeratorGetEnumerator(){returnnewCatEnumerator(datas);}}classCatEnumerator{//存储数据privatestring[]datas;//带参构造函数publicCatEnumerator(string[]datas){this.datas=datas;}publiccharCurrent{get;}publicboolMoveNext(){returntrue;}}

到目前为止我们已经设置了遍历的数据,如果要将数据遍历出来还需要一个下标索引来读取数组中的每个元素,并将每次读取出来的元素值赋值给Current属性。我们可以在迭代计数器对象中定义一个index整型私有属性作为下标索引属性,这里需要注意的是我们index这个属性的默认值为-1,这一点是很多新手开发人员比较容易出错的地方。既然有下标了,我们在遍历的时候下标就必须是递增变化,不断指向下一个元素的位置直到到达数组的末端为止。这时我们就需要在MoveNext方法中进行执行下标递增的操作了,MoveNext方法是一个返回值为bool类型的方法,其目的是告知foreach当前遍历的数据对象是否存在还未遍历到的元素,如果存在就返回true反之返回false遍历结束。下面我们针对这一段所说的内容进行代码编写。

classCatEnumerator{//存储数据privatestring[]datas;//带参构造函数publicCatEnumerator(string[]datas){this.datas=datas;}//数组下标privateintindex=-1;//遍历当前元素publiccharCurrent{get{returndatas[index];}}publicboolMoveNext(){index++;returnindexdatas.Length;returntrue;}}

到目前为止我们就编写了一个可以通过foreach遍历的类型,这里有三点很重要:

GetEnumerator方法的作用是foreach调用当前需要遍历的类型的迭代计数器对象,该方法的返回类型为用于foreach遍历的迭代计数器对象;Current属性就是当前遍历到的对象;MoveNext方法促使迭代计数器对象的计数移动到下一位。通过前面所述的内容,我们可知foreach遍历主要有三个步骤:

foreach调用当前可遍历类型的GetEnumerator方法创建一个迭代计数器对象,并将要遍历的数据传递给迭代计数器对象的构造函数中;迭代计数器对象调用它MoveNext方法将所以小标递增1,若下标大于数据长度则迭代完成;MoveNext方法返回true并返回Current属性中存储的数据。以上三个步骤总结起来就是获取迭代计数器对象调用MoveNext方法获取Current属性。

小技巧:在c#中如果要查看某个类型是否支持foreach我们可以查看该类型和该类型的迭代计数器是否都实现了IEnumerable接口,因为IEnumerable接口中的就包含了foreach实现的原理和必须调用的成员。


转载请注明原文网址:http://www.helimiaopu.com/jsyy/jsyy/12110.html
------分隔线----------------------------