使用yield 迭代datareader 可能不会关闭连接?
题
下面是一个示例代码,用于使用我在谷歌搜索时在一些地方找到的yield关键字从数据库检索数据:
public IEnumerable<object> ExecuteSelect(string commandText)
{
using (IDbConnection connection = CreateConnection())
{
using (IDbCommand cmd = CreateCommand(commandText, connection))
{
connection.Open();
using (IDbDataReader reader = cmd.ExecuteReader())
{
while(reader.Read())
{
yield return reader["SomeField"];
}
}
connection.Close();
}
}
}
我是否正确地认为,在这个示例代码中,如果我们不迭代整个 datareader ,连接就不会关闭?
如果我正确理解产量,这是一个不会关闭连接的示例。
foreach(object obj in ExecuteSelect(commandText))
{
break;
}
对于可能不是灾难性的数据库连接,我认为 GC 最终会清理它,但如果它不是连接而是更关键的资源怎么办?
解决方案
编译器合成的 Iterator 实现了 IDisposable,当退出 foreach 循环时 foreach 会调用它。
Iterator 的 Dispose() 方法将在提前退出时清除 using 语句。
只要您在 foreach 循环、using() 块中使用迭代器,或以其他方式调用 Dispose() 方法,就会发生迭代器的清理。
其他提示
连接将自动关闭,因为您在“using”块内使用它。
从我尝试过的简单测试来看,aku 是正确的,一旦 foreach 块退出,就会调用 dispose。
@大卫 :然而,调用堆栈在调用之间保留,因此连接不会关闭,因为在下一次调用时,我们将返回到yield之后的下一条指令,即while块。
我的理解是,当迭代器被释放时,连接也会随之被释放。我还认为不需要 Connection.Close,因为当由于 using 子句而处置对象时,它将被处理。
这是一个我试图测试其行为的简单程序......
class Program
{
static void Main(string[] args)
{
foreach (int v in getValues())
{
Console.WriteLine(v);
}
Console.ReadKey();
foreach (int v in getValues())
{
Console.WriteLine(v);
break;
}
Console.ReadKey();
}
public static IEnumerable<int> getValues()
{
using (TestDisposable t = new TestDisposable())
{
for(int i = 0; i<10; i++)
yield return t.GetValue();
}
}
}
public class TestDisposable : IDisposable
{
private int value;
public void Dispose()
{
Console.WriteLine("Disposed");
}
public int GetValue()
{
value += 1;
return value;
}
}
不隶属于 StackOverflow