2012-08-11 72 views
3

迭代器塊的延遲加載行爲正在緩存數據中造成困難。想想這個小測試程序:使用緩存迭代器塊結果的最佳做法

class Program 
{ 
    static IEnumerable<int> LoadDataFromDatabase() 
    { 
     Console.WriteLine("Hitting database...."); 
     yield return 13; 
    } 

    static IEnumerable<int> _cachedData = null; 
    static IEnumerable<int> CachedData 
    { 
     get 
     { 
      if (_cachedData == null) 
      { 
       _cachedData = LoadDataFromDatabase(); 
      } 
      return _cachedData; 
     } 
    } 

    static void Main(string[] args) 
    { 
     Console.WriteLine(string.Format("Collection contains {0} items.", CachedData.Count())); 
     Console.WriteLine(string.Format("Collection contains {0} items.", CachedData.Count())); 
    } 
} 

輸出到這是

擊中數據庫....

集合包含1項。

打數據庫....

集合包含1項。

我想只打一次數據庫(因此緩存),但由於LoadDataFromDatabase()是一個迭代塊,實際的數據庫調用是什麼緩存 - 而不是數據。

這種情況下的最佳做法是什麼?我應該只是做_cachedData = LoadDataFromDatabase().ToList()來存儲評估數據?

+2

您期望LoadDataFromDatabase'加載多少數據,它是否需要更改應用程序的生命週期?如果答案有點不變,那麼爲簡單起見,您應該將數據存儲爲列表。 – 2012-08-11 09:52:33

+0

我可以假設,如果我緩存數據,我確實需要整個列表,因此評估整個數據集總是可以的。而且我確實有一套適用於污染數據的機制。我想知道是否'.ToList()'是一種解決懶惰評估的標準方法,或者如果還有更多的建議(例如,有一些屬性我可以放在迭代器塊方法中告訴C#不要懶惰評估)。 – tenfour 2012-08-11 09:54:47

+0

你想緩存smth不要命中數據庫兩次 - 正確嗎? – 2012-08-11 09:59:57

回答

2

您可以添加.ToList()

static IEnumerable<int> CachedData 
{ 
    get 
    { 
     if (_cachedData == null) 
     { 
      _cachedData = LoadDataFromDatabase().ToList(); 
     } 
     return _cachedData; 
    } 
} 

的缺點是,如果在列表中的100.000項目,你這樣做:

var list1 = CachedData.Take(2).Sum(); 
var list2 = CachedData.Take(3).Sum(); 
var list3 = CachedData.Take(1).Sum(); 

...這將加載100.000項目名單。

解決的辦法是製作一個LazyList緩存可枚舉數您正在迭代,而不是提前。只需將.ToList()替換爲.ToLazyList()即可。

這導致最佳的選擇:

  • 第一3項僅加載總共1次。
  • 項目4從未加載

一個implementation of a lazy list is here的一個例子。

+0

我認爲你應該保持簡單,就像你在答案的第一部分所做的一樣。如果表現存在問題,他應該只考慮替代解決方案。 – 2013-10-12 21:25:08

+0

KISS是一個我非常喜歡的原則,它不會經常發生,每個循環都使用2個嵌套的迭代器。這是第一個簡單解決方案的很好的理由。然而,在某些情況下這是不正確的,所以我做了一個更復雜的版本,在所有(單線程)情況下都能正確工作。你可以選擇你的owm實現。 – 2013-10-13 07:35:38

相關問題