tags:

views:

386

answers:

1

After experimenting with an iterator block I noticed the generated IL code is not what I expect it to be. Instead of a try-finally block a try-fault block is generated, which I have never seen. I noticed that the compiler doesn't allow me use the fault keyword in 'handwritten' C#.

Is there any difference between the 2?

C# code:

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
}

The interesting line is the last line of the IL where a fault handler is specified, where in a normal try-finally block a finally handler is specified.

+5  A: 

Yes, a Finally block always executes on frame exit. A fault block executes only if an exception is unwound past the frame. The fault block in MoveNext preserves the using semantics for the case of an exception thrown from the try block of the ReadAllLines iterator. Some other mechanism must be in use to preserve the using semantics on normal exit from the iterator.

Steve Steiner
So the Dispose in the fault is only called when handling an exception in the try part. And the regular dispose is handled by the Dispose method of the generated IEnumerator.
pb