foreach可以对字符串数组,list集合等进行遍历,但是它如何对普通类进行遍历呢,使得我们可以对类中的对象属性进行操作呢。在这里我简单总结了三种方法,使得foreach可以对普通类进行遍历。
关于IEnumerable与IEnumerator
IEnumerable是一个非常简单的接口,它仅仅包含了一个返回IEnumerator接口对象的抽象方法:GetEnumerator()。而IEnumerator接口提供了一个属性和两个抽象方法分别是:
object Current { get; }
bool MoveNext();
void Reset();
Current表示遍历的集合的当前元素,MoveNext()实现移动Enumerator(枚举器)到下一个位置,Reset()顾名思义,即重置枚举器。
那么,这就说明实现该接口的集合都可以进行foreach遍历。所以我们会发现list集合有继承IEnumerable接口并实现了GetEnumerator()方法,从而实现了枚举器的功能。类似的Array等集合类都有实现该接口。那么我们所写的普通类只要实现了该接口,就可以进行foreach遍历。
一. 使用yield return
1. 实现
定义一个person类,如下:
class Person
{
public string name;
public int age;
public string sex;
public Person(string name, int age, string sex)
{
this.name = name;
this.age = age;
this.sex = sex;
}
}
然后定义一个PeopleInfo类,这个类中记录了多个Person对象,放在一个对象数组中。如果想遍历这个类获取这些对象信息,那就需要实现IEnumerable接口中的GetEnumerator()方法,如下:
class PeopleInfo:IEnumerable
{
public Person[] person;
public PeopleInfo(Person[] persons)
{
this.person = new Person[persons.Length];
for (int i = 0; i < persons.Length; i++)
{
this.person[i] = persons[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
for (int i = 0; i < person.Length; i++)
{
yield return person[i];
}
}
}
在GetEnumerator使用了yield return,它会在GetEnumerator中for每循环一次就会返回一个对象。编译器会生成一个状态机来维护迭代器的状态,所以yield return可以准确的知道要返回哪一个对象。
2. 测试
Person[] person ={
new Person("deas",15,"male"),
new Person("dad",18,"male"),
new Person("aliex",25,"female")
};
PeopleInfo personInfo = new PeopleInfo(person);
foreach (Person item in personInfo)
{
Console.WriteLine(item.name+" "+item.age+" "+item.sex);
}
Console.ReadKey();
- 结果
二. 借用Array类
改变上边的GetEnumerator方法为:
IEnumerator IEnumerable.GetEnumerator()
{
//表示返回array的GetEnumerator
return this.person.GetEnumerator();
}
可以知道Person是一个Array类的实例,所以直接返回该实例的GetEnumerator方法即可,在Array集合类中就有GetEnumerator的具体实现方法。这里测试与结果与上边的一样,不再做阐述。
三. 自己实现IEnumerator接口
前面说过foreach能够遍历集合是因为集合实现了IEnumerator接口方法那么我们自己也可以实现该接口,如下:
class PersonIEnumerator:IEnumerator
{
private int position = -1;
private PeopleInfo personInfo;
public PersonIEnumerator(PeopleInfo info)
{
this.personInfo = info;
}
public object Current
{
get
{
return personInfo.person[position];
throw new NotImplementedException();
}
}
public bool MoveNext()
{
position++;
return position < personInfo.person.Length;
throw new NotImplementedException();
}
public void Reset()
{
position = -1;
throw new NotImplementedException();
}
}
这里定义了一个专门遍历PeopleInfo 类的迭代器,在PersonIEnumerator类中实现了 IEnumerator 接口,然后实现GetEnumerator方法来返回IEnumerator对象如下:
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)new PersonIEnumerator(this);
}
然后进行测试可以得到相同的结果。
总结
可以发现第三种方法是比较麻烦的,但是它对IEnumerator 和IEnumerable的理解很有帮助,当然在实际看法中用前两种应该是很方便的。当然我们也可以让一个普通类继承某种类型的集合(list,Dictionary)或者实现(Ilist,IDictionary)的接口来完成对这个普通类的foreach遍历。