15

这是一个示例代码,用于使用我在谷歌搜索时在几个地方找到的 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();
        }
    }
}

我是否认为在这个示例代码中,如果我们不遍历整个数据读取器,连接就不会关闭?

如果我理解正确,这是一个不会关闭连接的示例。

foreach(object obj in ExecuteSelect(commandText))
{
  break;
}

对于可能不是灾难性的数据库连接,我想 GC 最终会清理它,但是如果它不是连接而是更关键的资源怎么办?

4

4 回答 4

12

编译器合成的 Iterator 实现IDisposable,在循环退出foreach时调用。foreach

Iterator 的方法将在提前退出Dispose()时清理语句。using

只要你在foreach循环、块中使用迭代器,或者以其他方式using()调用方法,迭代器的清理就会发生。Dispose()

于 2008-09-06T20:15:18.453 回答
2

连接将自动关闭,因为您在“使用”块中使用它。

于 2008-09-06T15:01:35.523 回答
2

从我尝试过的简单测试来看,aku 是对的,一旦 foreach 块退出,就会调用 dispose。

@David:但是调用堆栈保留在调用之间,因此连接不会关闭,因为在下一次调用时,我们将返回到 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;
    }
}
于 2008-09-06T19:10:11.743 回答
0

这个技术解释来看,您的代码不会按预期工作,而是在第二项上中止,因为在返回第一项时连接已经关闭。

@Joel Gauvreau:是的,我应该继续阅读。本系列的第 3 部分解释了编译器为 finally 块添加了特殊处理,以仅在实际结束时触发。

于 2008-09-06T18:10:16.660 回答