I'm using yield return
to iterate over an SqlDataReader
's records:
IEnumerable<Reading> GetReadings() {
using (var connection = new SqlConnection(_connectionString))
{
using (var command = new SqlCommand(_query, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return new Reading
{
End = reader.GetDateTime(0),
Value = reader.GetDouble(1)
};
}
}
connection.Close();
}
}
}
I'm then using an adapted version of this accepted answer to "zip" many iterators together:
var enumerators = data.Select(d => new
{
d.Key,
Enumerator = d.Value.GetEnumerator()
}).ToList();
while (true)
{
foreach (var item in enumerators)
{
if (!item.Enumerator.MoveNext())
{
yield break;
}
/*snip*/
}
/*snip*/
}
In the method above, the enumerator's Dispose()
is not explicitly called, and because they are not used within a using
or foreach
statement, would the underlying iterator remain in an open state? In my case with an open SqlConnection
.
Should I be calling Dispose()
on the enumerators to make sure the whole downstream chain is closed?
When enumerating over this iterator, if the enumerator's
Dispose()
is not explicitly called, and not used within ausing
statement, would the underlying iterator remain in an open state?
Let me re-phrase that question into a form that is easier to answer.
When using
foreach
to enumerate via an iterator block that contains ausing
statement, are the resources disposed of when control leaves the loop?
Yes.
What mechanisms ensure this?
These three:
A using
statement is just a convenient way to write a try-finally
where the finally
disposes of the resource.
The foreach
loop is also a convenient syntax for try-finally
, and again, the finally
calls Dispose
on the enumerator when control leaves the loop.
The enumerator produced by an iterator block implements IDisposable
. Calling Dispose()
on it ensures that all the finally
blocks in the iterator block are executed, including finally
blocks that come from using
statements.
If I avoid the
foreach
loop, callGetEnumerator
myself, and don't callDispose
on the enumerator, do I have a guarantee that thefinally
blocks of the enumerator will run?
Nope. Always dispose your enumerators. They implement IDisposable
for a reason.
Is that now clear?
If this subject interests you then you should read my long series on design characteristics of iterator blocks in C#.
http://blogs.msdn.com/b/ericlippert/archive/tags/iterators/
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