不使用,foreach或手动调用Dispose()时的枚举器处理
我正在使用yield return
迭代SqlDataReader
的记录:
IEnumerable 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(); } } }
然后我使用这个被接受的答案的改编版本来“拉”许多迭代器:
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()
以确保整个下游链关闭吗?
枚举这个迭代器时,如果未显式调用枚举器的
Dispose()
,并且未在using
语句中using
,那么底层迭代器是否会保持打开状态?
让我重新将这个问题改成一个更容易回答的forms。
当使用
foreach
通过包含using
语句的迭代器块进行枚举时,控制离开循环时是否会释放资源?
是。
什么机制确保这一点?
这三个:
-
using
语句只是编写try-finally
的便捷方式,finally
处理资源。 -
foreach
循环也是try-finally
一种方便的语法,同样,当控制离开循环时,finally
调用枚举器上的Dispose
。 -
迭代器块生成的枚举器实现了
IDisposable
。 在其上调用Dispose()
可确保执行迭代器块中的所有finally
块,包括finally
来自using
语句的块。
如果我避免使用
foreach
循环,请自己调用GetEnumerator
,不要在枚举器上调用Dispose
,我是否保证枚举器的finally
块将运行?
不。 始终处置您的普查员 。 他们实现IDisposable
是有原因的。
那现在清楚了吗?
如果这个主题让你感兴趣,那么你应该阅读我关于C#中迭代器块的设计特征的长篇系列。
您调用Dispose
以释放由连接分配的资源 ,并调用Close
以关闭连接。 在Close
调用之后,可以在连接池中推送连接,如果找到合适的运行时,在Dispose
情况下,它将被调度为destroy。
所以一切都取决于你所说的“有效”状态。
如果它被包含在using
dirrective中,那么就是try/finally
,你可以保证即使在迭代中发生任何exception,连接也将被关闭并且其资源将被销毁。 在其他情况下,你必须自己处理所有这些。