不使用,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() ,并且因为它们未在usingforeach语句中使用,底层迭代器是否会保持打开状态? 在我的情况下,打开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#中迭代器块的设计特征的长篇系列。

http://blogs.msdn.com/b/ericlippert/archive/tags/iterators/

您调用Dispose以释放由连接分配的资源 ,并调用Close以关闭连接。 在Close调用之后,可以在连接池中推送连接,如果找到合适的运行时,在Dispose情况下,它将被调度为destroy。

所以一切都取决于你所说的“有效”状态。

如果它被包含在using dirrective中,那么就是try/finally ,你可以保证即使在迭代中发生任何exception,连接也将被关闭并且其资源将被销毁。 在其他情况下,你必须自己处理所有这些。