我正在使用yield return
来迭代SqlDataReader
的记录:
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();
}
}
}
然后我使用this accepted answer的改编版本将多个迭代器“压缩”在一起:
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*/
}
在上面的方法中,未显式调用枚举器的Dispose()
,并且因为它们未在using
或foreach
语句中使用,底层迭代器是否仍处于打开状态?在我的情况下,打开SqlConnection
。
我应该在调查器上调用Dispose()
以确保整个下游链都已关闭吗?
答案 0 :(得分:17)
枚举这个迭代器时,如果未显式调用枚举器的
Dispose()
,并且未在using
语句中使用,那么底层迭代器是否会保持打开状态?
让我重新将这个问题改成一个更容易回答的形式。
当使用
foreach
通过包含using
语句的迭代器块进行枚举时,当控制离开循环时是否会释放资源?
是
有哪些机制可以确保这一点?
这三个:
using
语句只是编写try-finally
处理资源的finally
的便捷方式。
foreach
循环也是 try-finally
的方便语法,同样,finally
调用Dispose
枚举器当控制离开循环时。
迭代器块生成的枚举器实现IDisposable
。在其上调用Dispose()
可确保执行迭代器块中的所有finally
块,包括来自finally
语句的using
块。
如果我避开
foreach
循环,请自行致电GetEnumerator
,不要在调查器上调用Dispose
,我是否有finally
块的保证枚举器会运行吗?
不。 始终处置您的枚举器。他们出于某种原因实施IDisposable
。
现在清楚了吗?
如果这个主题感兴趣,那么你应该阅读我在C#中关于迭代器块的设计特征的长篇系列。
答案 1 :(得分:0)
您致电Dispose
取消分配连接分配的资源并致电Close
以关闭连接。在Close
调用之后,可以在连接池中推送连接,如果找到合适的运行时,在Dispose
的情况下,它将被安排销毁。
所以一切都取决于你所说的“有效”状态。
如果它包含在using
dirrective中,除了try/finally
之外没有其他任何内容,您可以保证即使在迭代中发生任何异常,连接也将被关闭和它的资源将被销毁。在其他情况下,你必须自己处理所有这些。
答案 2 :(得分:-1)
只需为此添加一些细微差别即可:
数组枚举器不是通用的,也不是一次性的
集合枚举器是通用的,并且是可抛弃的,但前提是T本身是引用类型...而不是值类型
因此,如果您使用GetEnumerator(),请务必使用
IEnumerator arrayEnumerator = arrayToEnumerate.GetEnumerator();
IEnumerator<T> collectionEnumerator = collectionToEnumerate.GetEnumerator();
然后在您的代码中
collectionEnumerator.Dispose(); // compiles if T is reference-type but NOT value-type
示例:
object[] theObjectArray = [...]
IEnumerator arrayEnumerator = theObjectArray.GetEnumerator(); // does not return
IEnumerator<object> and is not disposable
arrayEnumerator.Dispose(); // won't compile
List<double> theListOfDoubles = [...]
IEnumerator<someObject> doubleEnumerator = doubleEnumerator.GetEnumerator();
doubleEnumerator.Dispose(); // won't compile
List<someObject> theListOfObjects = [...]
IEnumerator<someObject> objectEnumerator = theListOfObjects.GetEnumerator(); // disposable
objectEnumerator.Dispose(); // compiles & runs