Search code examples
c#yield-return

'yield' enumerations that don't get 'finished' by caller - what happens


suppose I have

IEnumerable<string> Foo()
{
     try
     {

         /// open a network connection, start reading packets
         while(moredata)
         {
            yield return packet; 
        }
     }
     finally
      {
        // close connection 
      }
}

(Or maybe I did a 'using' - same thing). What happens if my caller goes

var packet = Foo().First();

I am just left with a leaked connection. When does the finally get invoked? Or does the right thing always happen by magic

edit with answer and thoughts

My sample and other 'normal' (foreach, ..) calling patterns will work nicely because they dispose of the IEnumerable (actually the IEnumerator returned by GetEnumerator). I must therefore have a caller somewhere thats doing something funky (explicitly getting an enumerator and not disposing it or the like). I will have them shot

the bad code

I found a caller doing

IEnumerator<T> enumerator = foo().GetEnumerator();

changed to

using(IEnumerator<T> enumerator = foo().GetEnumerator())

Solution

  • I am just left with a leaked connection.

    No, you're not.

    When does the finally get invoked?

    When the IEnumerator<T> is disposed, which First is going to do after getting the first item of the sequence (just like everyone should be doing when they use an IEnumerator<T>).

    Now if someone wrote:

    //note no `using` block on `iterator`
    var iterator = Foo().GetEnumerator();
    iterator.MoveNext();
    var first = iterator.Current;
    //note no disposal of iterator
    

    then they would leak the resource, but there the bug is in the caller code, not the iterator block.