1.foreach
foreach无需要知道集合中元素个数就可以迭代集合中的元素,它其实是迭代器模式的一个包装,就语言层面来说,是while的另一种形式。
展开
折叠
using System;
using System.Collections;
public class People : IEnumerable//版本1
{
string[] names;
public People(string[] names)
{
this.names = names;
}
public IEnumerator GetEnumerator()
{
return new Enumerator(this.names);
}
private class Enumerator : IEnumerator
{
private int state = -1;
string[] names;
internal Enumerator(string[] names)
{
this.names = names;
}
public bool MoveNext()
{
state++;
return (state < names.Length);
}
public void Reset()
{
state = -1;
}
public object Current
{
get
{
try
{
return names[state];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
}
}
public class Test
{
public static void Main()
{
People people = new People(new string[]{"aaa","bbb","ccc","ddd"});
foreach (var person in people)
{
Console.WriteLine(person);
}
IEnumerator Enumerator = people.GetEnumerator();
while (Enumerator.MoveNext())
{
Console.WriteLine(Enumerator.Current);
}
}
}
查看IL代码,先看foreach代码块的IL代码:
展开
折叠
IL_002f: callvirt instance class [mscorlib]System.Collections.IEnumerator People::GetEnumerator()
IL_0034: stloc.s CS$5$0001
.try
{
IL_0036: br.s IL_0046
IL_0038: ldloc.s CS$5$0001
IL_003a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_003f: stloc.1
IL_0040: ldloc.1
IL_0041: call void [mscorlib]System.Console::WriteLine(object)
IL_0046: ldloc.s CS$5$0001
IL_0048: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_004d: brtrue.s IL_0038
IL_004f: leave.s IL_0066
} // end .try
finally
{
IL_0051: ldloc.s CS$5$0001
IL_0053: isinst [mscorlib]System.IDisposable
IL_0058: stloc.s CS$0$0002
IL_005a: ldloc.s CS$0$0002
IL_005c: brfalse.s IL_0065
IL_005e: ldloc.s CS$0$0002
IL_0060: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0065: endfinally
} // end handler
再看while代码块的IL代码:
展开
折叠
IL_0071: callvirt instance class [mscorlib]System.Collections.IEnumerator People::GetEnumerator()
IL_0076: stloc.2
IL_0077: br.s IL_0084
IL_0079: ldloc.2
IL_007a: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_007f: call void [mscorlib]System.Console::WriteLine(object)
IL_0084: ldloc.2
IL_0085: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_008a: brtrue.s IL_0079
可以看出foreach语句块IL代码与while语句块的IL代码相比,只多了finally部分的资源处理,但循环部分的代码基本没有任何分别。
如果让private class Enumerator 增加IDisposable接口,将while语句块改写为如下:
展开
折叠
using((IDisposable)Enumerator)
{
while (Enumerator.MoveNext())
{
Console.WriteLine(Enumerator.Current);
}
}
得到的IL代码将与foreach基本没有区别。
由此可见,foreach是编译器给我们的一个语法糖,它包装了while。
2.yield
在上面的代码中枚举器private class Enumerator 是自己实现了IEnumerator接口中的方法,使用yield语句,可以让编译器自动实现,省去了自己写的麻烦(包含yield语句的方法或属性必须声明为返回IEnumerable 或IEnumerator ),所以上面的public class People 可以这样写
展开
折叠
public class People : IEnumerable//版本2
{
string[] names;
public People(string[] names)
{
this.names = names;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < names.Length; i++)
{
yield return names[i];
}
}
}
反编译之后,看IL代码:
这是版本1people类

这是版本2的people类
![U1PY(JNW_M%W@I~~953UI@Q[1] U1PY(JNW_M%W@I~~953UI@Q[1]](https://images.cnblogs.com/cnblogs_com/youngbo/201012/20101214002417944.jpg)
自己写的迭代器类Enumerator和编译器生成的迭代器类<GetEnumerator>d_0相比较,<GetEnumerator>d_0增加了IDisposable接口,另外就是增加了IEnumerator接口的泛型版本的继承。如果愿意,Enumerator也可以实现这两个接口。
用yield还可以返回IEnumerable接口类型
展开
折叠
using System;
using System.Collections;
public class People
{
string[] names;
public People(string[] names)
{
this.names = names;
}
public IEnumerable PeopleEven()
{
for (int i = 1; i < names.Length; i += 2)
{
yield return names[i];
}
}
}
public class Test
{
public static void Main()
{
People people = new People(new string[] { "aaa", "bbb", "ccc", "ddd" });
foreach (var person in people.PeopleEven())
{
Console.WriteLine(person);
}
}
}
该PeopleEven()返回的IEnumerable类<PeopleEven>d_0会自动实现GetEnumerator()方法和IEnumerator接口中的方法。看IL代码:
PeopleEven()返回的类<PeopleEven>d_0实现了IEnumerable和IEnumerator接口以及它们的泛型版本,还实现了IDisposable接口。因此,可以将这3个接口都实现,将类改写:
展开
折叠
using System;
using System.Collections;
public class People : IEnumerable, IEnumerator, IDisposable
{
string[] names = { "bbbb", "dddd" };
int state = -1;
public IEnumerator GetEnumerator()
{
return new People();
}
public bool MoveNext()
{
state++;
return (state < names.Length);
}
public void Reset()
{
state = -1;
}
public object Current
{
get
{
try
{
return names[state];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Dispose()
{
}
}
public class Test
{
public static void Main()
{
People people = new People();
foreach (var person in people)
{
Console.WriteLine(person);
}
}
}
看IL代码:
类People与PeopleEven()返回的类<PeopleEven>d_0基本相同,只是少了两个接口的泛型版本的实现。
由此可见,yield是一个更大的语法糖,它可以自动实现IEnumerable和IEnumberator接口中的方法,自动替我们实现迭代器。
用foreach和yield,可以非常方便地使用迭代模式。