不能不说的C#特性-迭代器(下),yield以及流的延迟计算
[2] 不能不说的C#特性-迭代器(下),yield以及流的延迟计算
[3] 不能不说的C#特性-迭代器(下),yield以及流的延迟计算
系列文章导航:
不能不说的C#特性-迭代器(下),yield以及流的延迟计算
走进Linq-Linq to SQL How do I(1)
走进Linq-Linq to SQL How do I(2)
走进Linq-Linq to SQL How do I(3)
走进Linq-Linq to SQL源代码赏析 Table
走进Linq-Linq to SQL源代码赏析之Provider的初始化
走进Linq-Linq to SQL源代码赏析,通过Linq to SQL看Linq
测试1的运算结果
测试WithNoYield():输出从0-19的数字
测试WithYield():什么都不输出
测试2的运算结果
测试WithNoYield():输出1-19接着输出3-19
测试WithYield():输出12334455…….
(为节省空间上面的答案没有原样粘贴,可以自己运行测试)
是不是感到很奇怪,为什么使用了yield的程序表现的如此怪异呢?
测试1中对WithYield()的测试,明明方法调用了,居然一行输出都没有,难道for循环根本没有执行?通过断点调试果然如此,for循环根本没有进去,这是咋回事?测试2中对WithYield()的测试输出是有了,不过输出怎么这么有趣?穿插着输出,在foreach遍历WithYield()的结果的时候,好像不等到最后一条遍历完,WithYield()不退出,这又是怎么回事?
还是打开IL代码瞧一瞧到底发生了什么吧
Main方法的IL代码:
{
.entrypoint
.maxstack 1
.locals init (
[0] int32 i,
[1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> CS$5$0000)
L_0000: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> TestLambda.Program::WithYield()
L_0005: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`10> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
L_000a: stloc.1
L_000b: br.s L_0020
L_000d: ldloc.1
L_000e: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
L_0013: stloc.0
L_0014: ldloca.s i
L_0016: call instance string [mscorlib]System.Int32::ToString()
L_001b: call void [mscorlib]System.Console::WriteLine(string)
L_0020: ldloc.1
L_0021: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_0026: brtrue.s L_000d
L_0028: leave.s L_0034
L_002a: ldloc.1
L_002b: brfalse.s L_0033
L_002d: ldloc.1
L_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0033: endfinally
L_0034: call string [mscorlib]System.Console::ReadLine()
L_0039: pop
L_003a: ret
.try L_000b to L_002a finally handler L_002a to L_0034
}
这里没什么稀奇的,在上一篇我已经分析过了,foreach内部就是转换成调用迭代器的MoveNext()方法进行while循环。我浏览到WithYield()方法:
{
return new <WithYield>d__0(-2);
}
晕,怎么搞的,这是我写的代码么?我的for循环呢?经过我再三确认,确实是我写的代码生成的。我心里暗暗叫骂,编译器,你怎么能这样“无耻”,在背后修改我的代码,你这不侵权么。还给我新生成了一个类d__0,这个类实现了这么几个接口:IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable(好啊,这个类将枚举接口和迭代器接口都实现了) 现在能解答测试1为什么没有输出了,调用WithYield()里面就是调用了一下d__0的构造方法,d__0的构造方法的代码:
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
这里没有任何输出。
在测试2中,首先我们会调用d__0的GetEnumerator()方法,这个方法里将一个整型局部变量<>1__state初始化为0,再看看MoveNext()方法的代码: