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())
It specifies that an iterator has come to an end. You can think of yield break as a return statement which does not return a value.
Incrementing an enumeration requires a cast to convert the integer result of addition back to the enumeration type, as in: d = day(d + 1); This seems to leave you with two less-than-perfect alternatives for writing loops that iterate over the day values.
Enumerations make for clearer and more readable code, particularly when meaningful names are used. The benefits of using enumerations include: Reduces errors caused by transposing or mistyping numbers. Makes it easy to change values in the future.
The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared.
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.
You would not end up with leaked connection. Iterator objects produced by yield return
are IDisposable
, and LINQ functions are careful to ensure proper disposal.
For example, First()
is implemented as follows:
public static TSource First<TSource>(this IEnumerable<TSource> source) { if (source == null) throw Error.ArgumentNull("source"); IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) return list[0]; } else { using (IEnumerator<TSource> e = source.GetEnumerator()) { if (e.MoveNext()) return e.Current; } } throw Error.NoElements(); }
Note how the result of source.GetEnumerator()
is wrapped in using
. This ensures the call to Dispose
, which in turn ensures the call of your code in the finally
block.
Same goes for iterations by foreach
loop: the code ensures disposal of the enumerator regardless of whether the enumeration completes or not.
The only case when you may end up with leaked connection is when you call GetEnumerator
yourself, and fail to properly dispose of it. However, this is a mistake in the code using IEnumerable
, not in the IEnumerable
itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With