以下是使用我在google搜索时在几个地方找到的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最终会将其清理干净,但是如果不是连接它会是一个更关键的资源呢?
答案 0 :(得分:11)
编译器合成的迭代器实现了IDisposable,当foreach循环退出时,它会调用。
Iterator的Dispose()方法将在早期退出时清除using语句。
只要在foreach循环中使用迭代器,使用()块或以其他方式调用Dispose()方法,就会发生迭代器的清理。
答案 1 :(得分:2)
连接将自动关闭,因为您在“使用”块中使用它。
答案 2 :(得分:2)
从我试过的简单测试开始,aku是对的,只要foreach块退出就调用dispose。
@David:但是调用堆栈保持在调用之间,因此连接不会被关闭,因为在下一次调用时我们将返回yield之后的下一条指令,即while块。
我的理解是,当处理迭代器时,连接也将随之处理。我还认为不需要Connection.Close,因为在使用子句时处理对象时会处理它。
这是一个我尝试测试行为的简单程序......
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;
}
}
答案 3 :(得分:0)
从this technical explanation判断,您的代码将无法按预期工作,但会在第二项上中止,因为在返回第一项时连接已经关闭。
@Joel Gauvreau:是的,我应该读一读。本系列的Part 3解释了编译器为finally块添加了特殊处理,仅在 real 端触发。