In a method returning IEnumerable<>
, I'm opening and looping over a resource (e.g. a database row reader). Once the loop finished, the resource is closed again.
However, it may happen that the caller decides not to finish the enumeration. This leaves the resource open.
Example:
IEnumerable<Foo> Bar ()
{
using (var r = OpenResource()) {
while (r.Read ()) {
yield return r;
}
}
}
// OK - this closes the resource again
foreach (var foo in Bar()) {
Console.WriteLine (foo);
}
// Not OK - resource stays open!
Console.WriteLine (Bar().First());
How would I solve this? Can I easily cancel an enumeration, i.e. tell it to skip over the rest of the loop, or dispose it (putting the cleanup code in Dispose
)?
I considered returning a Func<Result, bool>
so the user can have it return false
if he's done with iterating. Similarly, some kind of cancel token could be used, too. But both approaches seem cumbersome to me.
IEnumerable has not Count function or property. To get this, you can store count variable (with foreach, for example) or solve using Linq to get count.
IEnumerable is the return type from an iterator. An iterator is a method that uses the yield return keywords.
Normally it is the IEnumerator<>
that implements the IDisposable
, and if you look at the definition of IEnumerator<>
you'll see that:
public interface IEnumerator<out T> : IDisposable, IEnumerator
The foreach
statement correctly Dispose()
the IEnumerator<>
that receives from the IEnumerable<>
, so that:
IEnumerable<SomeClass> res = SomeQuery();
foreach (SomeClass sc in res)
{
if (something)
break;
}
upon exiting the foreach
in any way (the break
, an exception, naturally finishing res
), the Dispose()
of the IEnumerator<>
should be called. See https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx for an example of how the foreach
is implemented (a try
... finally
... with a Dispose()
inside the finally
)
Note that the C# will produce "correct" code for using
used inside a yield
function. See for example here: http://goo.gl/Igzmiz
public IEnumerable<Foo> Bar()
{
using (var r = OpenResource())
{
while (r.Read ())
{
yield return new Foo();
}
}
}
is converted to something that
void IDisposable.Dispose()
{
int num = this.<>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
this.<>m__Finally1();
}
}
}
The Dispose()
method of IEnumerator<>
will call a m__Finally1
method that will (IDisposable)this.<r>5__1.Dispose();
(where 5__1
is the r
returned from OpenResource()
). The m__Finally
is even called if the code simply "exits" the while (r.Read ())
:
if (!this.<r>5__1.Read())
{
this.<>m__Finally1();
and/or if there is an exception.
catch
{
this.System.IDisposable.Dispose();
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