2010-11-27 30 views
2

我有以下代碼。查詢需要大約30秒才能執行。我發現每次調用頂層using塊時for each都會嘗試枚舉迭代器。對枚舉器中的每個調用MoveNext執行哪個查詢。爲什麼在包含yield return的函數中重複使用(){}塊?

那麼爲什麼懶惰的評估一次又一次地重塑using。我知道周圍的工作,但我需要了解這個目的。

private IEnumerable<FriendProfile> GetProfiles() { 
     var query = @" 
      SELECT VirtualNumber, City, Gender, Nick, Age 
      FROM Profiles 
      WHERE Not VirtualNumber In (SELECT VirtualNumber FROM Messages) 
      ORDER BY Id DESC;"; 

     using (var con = GetConnection()) { 
      using (var cmd = dbconnection.CreateCommand()) { 
       cmd.CommandText = query; 
       var reader = cmd.ExecuteReader(); 
       while (reader.Read()) { 
        var fp = new FriendProfile(); 
        fp.VirtualNumber = reader.GetString(0); 
        fp.City = reader.GetString(1); 
        fp.Gender = reader.GetString(2); 
        fp.Nick = reader.GetString(3); 
        fp.Age = reader.GetInt16(4).ToString(); 
        yield return fp; 

       } 
      } 
     } 
    } 


    foreach(var fp in GetProfiles()){ 
    .... //foreach item using(){} in GetProfile() is reinitializes. All usings blocks 
    } 

回答

4

我不是100%確定哪個使用了你所指的塊,但是如果我理解正確,你所說的話不應該發生。 yield將從GetProfiles()方法返回執行控制,但yield(即using塊的結尾)之後的代碼將不會執行,直到while條件爲false

下面是一個簡單的例子,應表現出這種行爲:

使用此類顯示當using塊的結束執行:

public class Disposable : IDisposable 
{ 
    private readonly string name; 

    public Disposable(string name) 
    { 
     this.name = name; 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("Disposing of {0}", name); 
    } 
} 

和此代碼:

private IEnumerable<int> Test() 
{ 
    using (new Disposable("outer")) 
    { 
     using (new Disposable("inner")) 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       yield return i; 
      } 
     } 
    } 
} 

... 

foreach (int i in Test()) 
{ 
    Console.WriteLine("item {0}", i); 
} 

輸出:

 
item 0 
item 1 
item 2 
item 3 
item 4 
item 5 
item 6 
item 7 
item 8 
item 9 
disposing of inner 
disposing of outer 

這表明在退出for循環之前,using塊不會退出。

+0

你對我是一個錯誤。 `IEnumerable a = Test();`我在多個循環中使用了多個時間。爲每個調用`a.GetEnumerator()`迭代,這就是爲什麼以前的枚舉器是垃圾收集或處置。我應該保存`var b = Test()。GetEnumerator()`並多次使用它。 – affan 2010-11-27 21:24:04

0

這是因爲yield得到編譯的方式。它實際上是一個迭代的調用,爲了實現由GetProfiles()方法定義的集合而創建的類。通過您的foreach,每一步都會調用一次MoveNext()This是一個體面的介紹幕後發生的事情。

+0

剛剛測試過它。最後(並因此使用)只被調用一次。可能當迭代器到達末端或被處置時。 – CodesInChaos 2010-11-27 20:54:49

相關問題