【问题标题】:Using yield to iterate over a datareader might not close the connection?使用 yield 遍历数据读取器可能不会关闭连接?
【发布时间】:2010-09-08 00:46:05
【问题描述】:

这是一个示例代码,它使用我在谷歌搜索时在几个地方找到的 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 最终会清理它,但如果它不是连接而是更关键的资源怎么办?

【问题讨论】:

    标签: .net-2.0 yield


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      this technical explanation来看,你的代码不会像预期的那样工作,而是在第二个项目上中止,因为在返回第一个项目时连接已经关闭。

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

      【讨论】:

        【解决方案3】:

        从我尝试过的简单测试来看,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;
            }
        }
        

        【讨论】:

          【解决方案4】:

          编译器合成的迭代器实现IDisposable,当退出foreach循环时foreach调用。

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

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

          【讨论】:

            猜你喜欢
            • 2012-01-21
            • 2019-03-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-12
            相关资源
            最近更新 更多