不能不说的C#特性-迭代器(上)及一些研究过程中的副产品
系列文章导航:
不能不说的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
调用迭代器的MoveNext()方法,L_004e: brtrue.s L_0036 如果是true的话跳转,
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)
获取当前值,然后输出看到没有,实际foreach后面干的事就是获取迭代器,然后一个while循环,不过这样一些确实简洁多了。说到这里是不是
while (ienumerator.MoveNext())
{
object item = ienumerator.Current;
Console.WriteLine(item.ToString());
}
和
{
Console.WriteLine(item.ToString());
}
这两样代码是一样的呢?如果不一样那推荐使用哪一个呢?当然是使用第二种,简洁嘛,除了简洁之外就没有其它的了?细心读者会发现上面的IL代码,在结束循环后还有一大块,可我们的C#代码中并没有啊,接着分析:
.try L_0034 to L_0052 finally handler L_0052 to L_0063
这里说明从L_0034到L_0052是被放在try里面的,恰好这段代码是循环体里的东西,L_0052到L_0063里是放在finally里的,看来foreach还给我们加了一个try{}finally{}结构啊。那看看L_0052到L_0063里是什么东西吧:
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()
判断迭代器对象是否是一个IDisposable实例,如果是,那就要调用它的Dispose()方法了(为啥它要实现IDisposable接口?那肯定这个迭代器里使用了一些非托管资源)。 看到了吧,foreach也真够智能的,看来使用foreach的方式是比自己用while方式安全稳定多了。
(PS:好像是扯远了点,不过大家一起了解一下,呵呵,其实我当初也没想说这个,不过后来看IL代码有点不对劲,就当作个副产品吧)C# 2.0里还出现个关键字yield,我看了半天MSDN也没明白它的意思:
在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。到现在还是没明白,不过yield这个东西后面却包含了很多东西,有一些非常“奇怪”的特性, 我称之为奇怪的意思是与我们以前的思维有的不符,Linq的一些特质也是建立在这个特性之上的。关于yield的更多讨论我想放在另外一篇文章中,因为我觉得有必要。