2009-09-10 65 views
19

突破值得關注的是從有一個foreach打破有道使得IEnumerable的<>知道,我做它應該清理。關於收益率的回報,並從一個foreach

考慮下面的代碼:

private static IEnumerable<Person> getPeople() 
    { 
     using (SqlConnection sqlConnection = new SqlConnection("...")) 
     { 
      try 
      { 
       sqlConnection.Open(); 
       using (SqlCommand sqlCommand = new SqlCommand("select id, firstName, lastName from people", sqlConnection)) 
       { 

        using (SqlDataReader reader = sqlCommand.ExecuteReader()) 
        { 
         while (reader.Read()) 
          yield return new Person(reader.GetGuid(0), reader.GetString(1), reader.GetString(2)); 
        } 
       } 
      } 
      finally 
      { 
       Console.WriteLine("finally disposing of the connection"); 
       if (sqlConnection.State == System.Data.ConnectionState.Open) 
        sqlConnection.Close(); 
      } 
     } 
    } 

如果他的消費不會在foreach突破則寄託都是好的,讀者將返回false,while循環willend和函數清理數據庫命令和連接。但是如果在我完成之前調用者從這個foreach中斷了,會發生什麼?

+1

也看到http://stackoverflow.com/questions/1400146/are-there-any-pitfalls-to-using-an-ienumerablet-return-type-for-sql-data/1400195# 1400195 – 2009-09-10 16:36:47

回答

30

非常好的問題。你不需要擔心這個;編譯器會爲您處理它。基本上,我們所做的是將finally塊的清理代碼放入生成的迭代器的特殊清理方法中。當控制離開調用者的foreach塊時,編譯器會生成調用迭代器上的清理代碼的代碼。

一個簡單的例子:

static IEnumerable<int> GetInts() 
{ 
    try { yield return 1; yield return 2;} 
    finally { Cleanup(); } 
} 

你的問題基本上是 「被稱爲在這種情況下清理()?」

foreach(int i in GetInts()) { break; } 

是的。迭代器塊與調用清理Dispose方法類生成的,然後在foreach循環類似的東西產生:

{ 
    IEnumerator<int> enumtor = GetInts().GetEnumerator(); 
    try 
    { 
    while(enumtor.MoveNext()) 
    { 
     i = enumtor.Current; 
     break; 
    } 
    } 
    finally 
    { 
    enumtor.Dispose(); 
    } 
} 

所以,當突破發生,最終接管並處置被稱爲。

見我最近的系列文章,如果你想了解一些我們在這個功能的設計中考慮了怪異的極端案例的詳細信息。

http://blogs.msdn.com/ericlippert/archive/tags/Iterators/default.aspx

+0

這是一個很好的答案! – 2009-09-11 02:45:02

2

您可以使用的語句

yield break; 

提前跳出產量環的,但你的代碼揭示了一個誤區,我認爲...... 當您使用「使用」的聲明,

using (SqlConnection sqlConnection = new SqlConnection("...")) 
{ 
    // other stuff 
} 

你自動嘗試finally塊中編譯的IL代碼,finnaly塊將調用Dispose,並在Dispose代碼中連接將被關閉...

+0

小幅修正:「收益率突破」是一種說法。 – jason 2009-09-10 15:19:30

+0

修改爲 – 2009-09-10 15:20:22

2

讓我們看看我是否得到您的問題。

foreach(Person p in getPeople()) 
{ 
    // break here 
} 

由於foreach關鍵字的原因,Enumerator已妥善處置。在處理Enumerator期間,getPeople()的執行被終止。所以連接被正確清理。