使用yield迭代datareader可能无法关闭连接?

时间:2008-09-06 14:53:45

标签: .net-2.0 yield

以下是使用我在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最终会将其清理干净,但是如果不是连接它会是一个更关键的资源呢?

4 个答案:

答案 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 端触发。