迭代器块在IL中生成try-fault。

12

尝试了迭代器块后,我注意到生成的IL代码不是我所期望的。生成了一个try-fault块,而不是我从未见过的try-finally块。我注意到编译器不允许我在“手写”的C#中使用fault关键字。

这两者有什么区别吗?

C#代码:

static IEnumerable<string> ReadAllLines(string fileName)
{
    using (var file = System.IO.File.OpenText(fileName))
    {
        string s;
        while ((s = file.ReadLine()) != null)
        {
            yield return s;
        }
    }
}

中间语言代码(MSIL Code):

.method private hidebysig newslot virtual final instance bool MoveNext() cil managed
{
    .override [mscorlib]System.Collections.IEnumerator::MoveNext
    .maxstack 3
    .locals init (
        [0] bool CS$1$0000,
        [1] int32 CS$4$0001,
        [2] string CS$0$0002,
        [3] bool CS$4$0003)
    L_0000: ldarg.0 

    // try body

    L_008d: leave.s L_0097
    L_008f: ldarg.0 
    L_0090: call instance void ConsoleApplication2.Program/<ReadAllLines>d__0::System.IDisposable.Dispose()
    L_0095: nop 
    L_0096: endfinally 
    L_0097: nop 
    L_0098: ldloc.0 
    L_0099: ret 
    .try L_0000 to L_008f fault handler L_008f to L_0097
}

有趣的那行代码位于IL的最后一行,其中指定了一个故障处理程序,在正常的try-finally块中指定了一个finally处理程序。


1
为什么这个标签特别标注了 .net-4.0?这在版本之间有所改变吗? - Alexey Romanov
1个回答

8

是的,Finally块总是在帧退出时执行。只有在异常被解除帧时,故障块才会执行。MoveNext中的fault块保留了从ReadAllLines迭代器的try块抛出异常的情况下使用语义。对于从迭代器正常退出,必须使用其他机制来保留使用语义。


因此,在故障中的Dispose仅在try部分处理异常时调用。而常规的dispose由生成的IEnumerator的Dispose方法处理。 - Paul van Brenk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接