问题
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?
回答1:
When enumerating over this iterator, if the enumerator's
Dispose()is not explicitly called, and not used within ausingstatement, 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
foreachto enumerate via an iterator block that contains ausingstatement, are the resources disposed of when control leaves the loop?
Yes.
What mechanisms ensure this?
These three:
A
usingstatement is just a convenient way to write atry-finallywhere thefinallydisposes of the resource.The
foreachloop is also a convenient syntax fortry-finally, and again, thefinallycallsDisposeon the enumerator when control leaves the loop.The enumerator produced by an iterator block implements
IDisposable. CallingDispose()on it ensures that all thefinallyblocks in the iterator block are executed, includingfinallyblocks that come fromusingstatements.
If I avoid the
foreachloop, callGetEnumeratormyself, and don't callDisposeon the enumerator, do I have a guarantee that thefinallyblocks 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/
回答2:
You call Dispose to deallocate resources allocated by the connection and call Close to just close the connection. AfterClose call the connection may be pushed in connection pool, if runtime that found suitable, in case of Dispose, instead, it will be scheduled for destroy .
So all depends on what you mean saying "valid" state.
If it's enclosed in usingdirrective, which is nothing else then try/finally, you have a guarantee that even if any exception happen in iteration the connection will be closed and its resources will be destroyed. In other cases you have to handle all it by yourself.
来源:https://stackoverflow.com/questions/21337340/enumerator-disposal-when-not-using-using-foreach-or-manually-calling-dispose