2008-09-06 52 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; 
    } 
}