.netframerwork中的枚举器
我们可以使用foreach迭代数组,集合。那么为什么数组和集合可以使用foreach迭代而别的类型却不行呢?foreach的内部机制又是什么,这里我来说一下枚举器。
很多教材上说,使用foreach迭代必须满足什么条件,很多人就是一句话,实现IEumerable接口。其实不是完全正确。实现IEumerable接口只是第一步。第二步是要实现GetEumerator()方法,这个方法需要返回IEumerator接口。其实真正起作用的是GetEumerator()和IEumerator接口。原因是,C#中的foreach最后在IL代码中不会编译为foreach,而是会把foreach转化为IEumnerator接口的方法和属性.先看看IEumerator的几个属性和方法。
MoveNext():MoveNext方法移动到集合的下一个元素,如果有这个元素,返回true,如果集合不再有任何元素,返回false
Current:返回光标所在的元素
Reset():将光标重新定位与集合的开头
我们来看看具体的代码是怎么实现的。
首先我们定义一个集合,集合是实现了IEumerable接口,和实现了返回IEumerator的GetEumerator()方法。所以我们可以使用foreach迭代。
还是实体类。
[Serializable]
public class Person
{
private int _personId;
public int PersonId
{
get { return _personId; }
set { _personId = value; }
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public Person() { }
public Person(int id, string firstName, string lastName)
{
this._personId = id;
this._firstName = firstName;
this._lastName = lastName;
}
}
调用代码
static void Main(string[] args)
{
List<Person> persons = new List<Person>();
persons.Add(new Person(1,"Edrick","Liu"));
persons.Add(new Person(2,"Meci","Luo"));
persons.Add(new Person(3, "Jun", "Ma"));
foreach (Person person in persons)
{
Console.WriteLine("PersonId:{0},FirstName:{1},LastName:{2}",person.PersonId,person.FirstName,person.LastName);
}
Console.Read();
}
迭代了这个集合,实际上foreach在编译的时候会翻译成下面的代码
static void Main(string[] args)
{
List<Person> persons = new List<Person>();
persons.Add(new Person(1,"Edrick","Liu"));
persons.Add(new Person(2,"Meci","Luo"));
persons.Add(new Person(3, "Jun", "Ma"));
IEnumerator<Person> eumerator = persons.GetEnumerator();
try
{
while (eumerator.MoveNext())
{
Person p = eumerator.Current;
Console.WriteLine("PersonId:{0},FirstName:{1},LastName:{2}", p.PersonId, p.FirstName, p.LastName);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
eumerator.Reset();
}
Console.Read();
}
foreach也只是这种方式的简便调用,那么我们该怎么实现IEumerable接口的GetEumerator()方法呢?下面我们来看看怎么实现。
首先我们介绍一个关键字,yield关键字
yield:在迭代器块中用于向枚举对象提供值或结束迭代。一般跟return和break配合使用
yield return:返回集合中的一个元素,并移动到下一个元素。
yield break:停止迭代
那么什么是迭代块?
迭代块:包含yield语句的方法或者属性称为迭代块,迭代块必须声明为返回IEumerator或者IEumerable。
还是先看一个简单的示例
public class StringCollection:IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return "Edrick";
yield return "Liu";
}
}
调用代码
static void Main(string[] args)
{
StringCollection coll = new StringCollection();
foreach (string s in coll)
{
Console.WriteLine(s);
}
Console.Read();
}
这里其实还是我们先前介绍的,foreach的原理。那么迭代块是怎么运行的呢,为什么我们yield return的时候枚举器可以识别呢?
public class StringCollection:IEnumerable
{
public IEnumerator GetEnumerator()
{
Enumererator en = new Enumererator(0);
return en;
}
public class Enumererator : IEnumerator
{
private int state;
private object current;
public Enumererator(int state) { this.state = state; }
object IEnumerator.Current
{
get { return current; }
}
bool IEnumerator.MoveNext()
{
switch (state)
{
case 0:
current = "Edrick";
state = 1;
return true;
case 1:
current = "Liu";
state = 2;
return true;
case 2:
break;
}
return false;
}
void IEnumerator.Reset()
{
throw new NotImplementedException();
}
}
}
迭代块会编译成一个yield类型,就是我们这里定义的类Enumererator,继承自IEnumerator。 定义了一个状态机,一个元素。每次迭代一个元素,状态机会加1.然后把我们定义的值赋给 current。current有由Current返回。最后,在GetEnumerator()返回这个类。这就是yield的实现机制。所以,我们可以利用yield做一些别的事情
public class MuTitle
{
string[] names = { "Edrick Liu","Meci Luo","Ma Jun","XiongKaiHeng"};
public IEnumerator GetEnumerator()
{
for (int i = 0; i < names.Length; i++)
{
yield return names[i];
}
}
public IEnumerable Reverse()
{
for (int i = names.Length-1; i >=0; i--)
{
yield return names[i];
}
}
public IEnumerable Subset(int index, int length)
{
for (int i = index; i < index + length; i++)
{
yield return names[i];
}
}
}
上面有两个方法返回的是IEnumerable,是为了包装我们的枚举器。实际等效于
public IEnumerable Reverse()
{
Enumerable e = new Enumerable();
return e;
}
public class Enumerable :IEnumerable
{
public IEnumerator GetEnumerator()
{
MuTitle m = new MuTitle();
for (int i = m.names.Length - 1; i >= 0; i--)
{
yield return m.names[i];
}
}
}

浙公网安备 33010602011771号