C#迭代器还是比较常见的东西,这里我们主要介绍C#迭代器局部变量,包括介绍C#里出现了foreach关键字等方面。
看看***的测试,是不是不管具体的集合如何改变,遍历代码都非常稳定?而且扩展新的集合类也非常方便,只是添加代码不会修改原来的代码,符合开闭原则。当然,这么好的解决方案微软当然不会放过,现在C# 2.0里已经内置了对C#迭代器的支持,看看System.Collections, System.Collections.Generic命名空间,所有的集合都实现了这个接口:IEnumerable,这个接口还有泛型的版本。注意到这个接口只有一个方法:IEnumerator GetEnumerator();,IEnumerator就是C#迭代器的接口,相当于我的实例里面的Iterator,它也有泛型的版本。
那么现在在.net里所有的集合类都可以这样访问了:
IEnumerator ienumerator = list.GetEnumerator();
while(ienumerator.MoveNext())
{
object current = ienumerator.Current;
}
- 1.
- 2.
- 3.
- 4.
- 5.
但是这样访问也太麻烦了,所以C#里出现了foreach关键字,我们来看看foreach背后发生了什么
public static void Main()
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add(3);
foreach (object item in list)
{
Console.WriteLine(item.ToString());
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
下面是它对应的IL代码:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] class [mscorlib]System.Collections.ArrayList list,
[1] object item,
[2] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
[3] class [mscorlib]System.IDisposable CS$0$0001)
L_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
L_0005: stloc.0
L_0006: ldloc.0
L_0007: ldc.i4.1
L_0008: box int32
L_000d: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
L_0012: pop
L_0013: ldloc.0
L_0014: ldc.i4.2
L_0015: box int32
L_001a: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
L_001f: pop
L_0020: ldloc.0
L_0021: ldc.i4.3
L_0022: box int32
L_0027: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
L_002c: pop
L_002d: ldloc.0
L_002e: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]
System.Collections.ArrayList::GetEnumerator()
L_0033: stloc.2
L_0034: br.s L_0048
L_0036: ldloc.2
L_0037: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
L_003c: stloc.1
L_003d: ldloc.1
L_003e: callvirt instance string [mscorlib]System.Object::ToString()
L_0043: call void [mscorlib]System.Console::WriteLine(string)
L_0048: ldloc.2
L_0049: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_004e: brtrue.s L_0036
L_0050: leave.s L_0063
L_0052: ldloc.2
L_0053: isinst [mscorlib]System.IDisposable
L_0058: stloc.3
L_0059: ldloc.3
L_005a: brfalse.s L_0062
L_005c: ldloc.3
L_005d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0062: endfinally
L_0063: call string [mscorlib]System.Console::ReadLine()
L_0068: pop
L_0069: ret
.try L_0034 to L_0052 finally handler L_0052 to L_0063
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
从.locals init 那里可以看出编译器为我们添加了两个C#迭代器局部变量,一个就是C#迭代器。
L_002d: ldloc.0
L_002e: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]
System.Collections.ArrayList::GetEnumerator()
L_0033: stloc.2
- 1.
- 2.
- 3.
- 4.
这三行代码告诉我们,调用list的GetEnumerator()方法,获取C#迭代器实例将其赋值给编译器为我们添加的那个C#迭代器局部变量,接着是L_0034: br.s L_0048,br.s这个指令是强制跳转,我们接着看
L_0048: ldloc.2
L_0049: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
- 1.
- 2.
【编辑推荐】