枚举:
在foreach中使用迭代块对集合和数组进行枚举,无需知道集合数组中具体的元素个数。
原因是 数组和集合实现了带GetEnmumerator()方法的IEumerable接口。GetEnumerator()
实现了IEumnerable接口的枚举。
和foreach接口枚举相关的两个重要接口为:
IEnumerable 和IEnumerator(它们也都支持泛型)
IEnumerable接口源代码定义:
interface IEnumerable
{
//返回一个类型为IEnumerator的计算器
IEnumerator GetEnumerator();
}
IEnumerator接口的源代码定义:
Interface IEnumerator
{
//当前枚举的对象
Object Current{get;}
//确定枚举对象是否存在,并将计数器指到下一个枚举对象
bool MoveNext();
//Reset();
}
我们发现IEnumerable接口中定义的是一个方法名为GetEnumerator,返回类型为IEnumerator的方法定义。
紧接着 IEnumerator接口中就定义了Current ,MoveNext()等属性和方法,也就是说,你实现了IEnmerable接口
就必须要实现Current ,MoveNext(),Reset()的方法。
GetEnumerator()用接口IEnumerable接口定义,foreach并不需要被迭代的对象必须实现这个接口,
只需要该对象中有一个名为GetEnumerator(),且返回类型为IEnumerator的方法就行。
foreach语句:
C#中foreacach语句不会解析为IL中的foreach语句,C#编译器会把foreach语句解析为实现了IEnumerable接口的方法和属性
Peson[ ] Persons = { new Person("tom",21),new Person("jack",25)};
foreach (person p in persons)
{
Console.WriteLine(P.FirstName);
}
这条语句并不是原本不动的映射到IL中,它会转化为一个这样的模式。
IEnumerator<Person> MyEnum = Persons.GetEnumerator();
while(MyEnum.MoveNext())
{
Person p = MyEnum,Current;
Console.WriteLine(p.FirstName);
}
使用foreach对数据进行迭代时,首先会调用GetEnumerator()方法,来返回一个类型为IEnumerator的计数器,
接着使用技术器的MoveNext()方法对数据中的每一项数据进行枚举。
yield语句:
yield语句是C#2.0时引入的一个语法,它能使我们轻松的创建一个枚举器。这是为了简化实现枚举器的步骤,否则你需要实现IEnumerable和IEnumerator两个接口。
public class hellcollection
{
public IEnumerator<string> GetEnumerator()
{
yield return "hello";
yield return "world";
}
}
我们就可以使用foreach语句对hellocollection对象进行迭代了
var hellco = new hellection();
foreach(string s in hellco)
{
Console.WriteLine(s);
}
那么yield return语句到底帮我们做了些什么事情,可以使我们能够使用foreach对该对象进行迭代呢?
在C#中使用yield return语句,会生成一个包含一个状态机的迭代类型计数器。
yield 类型实现了IEnumerator和IDispose接口。我们可以把yield看成是C#编译器帮我们实现了IEnumerator接口
和IDispose接口的一个对象。
上面的例子,实际实现是下面这样的情况。
public class hellcollection
{
public IEnumerator GetEnumerator()
{
return Enumerator(0);//返回一个计数器
}
public class Enumerator:IEnumerator<string>,IDispose,IEnumerator//定义一个内部类来实现枚举器的属性和方法
{
private int state ;//状态机表示当前美剧状态
private string current;//当前枚举的对象值
public Enumerator(int state)
{
this.state = state;
}
public bool MoveNext()
{
swith(state)
{
case 0:
current="hello";
state=1;
return true;
case 1:
current="world";
state=2;
return true;
case 3:
break;
}
return false;
}
public string Current
{
get{return current;}
}
//...........还有一些别的接口的方法实现 我就不写了。
}
}
迭代集合的不同方式:
可以使用yield return语句,以不同的方式迭代集合
public class MusicTitles
{
string[ ] names = {"套马杆","凤凰传奇","伤不起"};
//正序迭代
public IEnumerator<string> GetEnumerator()
{
for(int i=0;i<names.Length;i++)
{
yield return names[i];
}
}
//逆序迭代
public IEnumerable<string> Reverse()
{
for(var i=names.Length;i>=0;i--)
{
yield return names[i];
}
}
//部分迭代
public IEnumerable<string> Sub(int index,int length)
{
for(int i=index;i<index+Length;i++)
{
yield return names[i];
}
}
}
第一个迭代的类型的返回值为IEnumerator,其他的则为IEnumerable 我的个人理解是
IEnumerator已经为我们定义好了迭代模板,无需再次定义,所以后面的迭代直接调用其
定义好的模板即可,无需在使自己的返回类型为IEnumerator ,而为IEnumerable即可。
类支持的默认迭代返回计数器的方法为GetEnumerator()方法。
下面我们对上述例子进行各种形式的迭代。
第一种:
var musicEnum = new MusicTitles();
foreach(var title in musicEnum)
{
Console.WriteLine(title);
}
第二种:
foreach(var title in musicEnum.Reverse())
{
Console.WriteLine(title);
}
第三种:
foreach(var title in musicEnum.Sub(1,2))
{
Console.WriteLine(title);
}
---------------------- Windows Phone 7手机开发、 .Net培训、期待与您交流! ----------------------
详细请查看: http://net.itheima.com/