2008-09-06 20 views
15

下面是一个示例代码,而周围的Googling检索使用,我在几个地方发现yield关键字数据库中的数据:使用yield来遍历datareader可能不会关闭连接?

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将清理最后,但如果不是连接而是更重要的资源呢?

回答

11

编译器synthesises迭代器实现IDisposable,其中的foreach呼叫时foreach循环退出。

Iterator的Dispose()方法将清理早期退出时的using语句。

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

2

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

0

this technical explanation判断,您的代码不会按预期工作,但会在第二项中中止,因为连接在返回第一项时已经关闭。

@Joel Gauvreau:是的,我应该阅读。本系列的Part 3解释说,编译器为finally块添加特殊处理,以便仅在实际端触发。

2

从我试过的简单测试中,aku是正确的,只要foreach块退出就调用配置。

@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; 
    } 
}