2012-03-12 17 views
2

這是我的代碼:使用.First()和.Count()打破了IEnumerable?

protected IEnumerable<Hotel> Hotels; 
protected Hotel Hotel; 

Hotels = from Hotel hotel in new Hotels() 
     select hotel; 

Response.Write("RES " + Hotels.Count() + "<br />"); 
if (Hotels.Count() > 0) 
{ 
    Hotel = Hotels.First(); 
} 
Response.Write("RES " + Hotels.Count() + "<br />"); 
Response.Write("RES " + Hotels.Count() + "<br />"); 

好,酒店有它1項。但結果如下:

RES 1 
RES 0 
RES 1 

爲什麼?似乎.First()與迭代器混淆?我怎樣才能解決它使用IEnumberable? (不使用另一種列表,我需要IEnumerable)。

+6

酒店是如何填充的以及底層存儲空間是什麼它實際上是參考一個列表? – 2012-03-12 09:37:49

+1

行爲取決於酒店究竟是什麼。如果它包含一些可以更改的數據源的引用/連接,那麼顯然這個值可以在對Count的調用之間改變,或者如果使用yield return實現它,它可以做任何事情。嘗試對它調用ToArray(),然後對ToArray()返回的值進行計數等操作,然後知道該集合有固定的快照。 – Foo42 2012-03-12 09:38:53

+1

酒店的實際實施可能會造成問題。例如,它適用於列表。 – Botz3000 2012-03-12 09:39:06

回答

3
static void Main(string[] args) 
{ 
    IEnumerable<String> Hotels = new List<String>{"sdsfsdf"}; 
    String Hotel; 

    Console.WriteLine("RES " + Hotels.Count()); 
    if (Hotels.Count() > 0) 
    { 
     Hotel = Hotels.First(); 
    } 
    Console.WriteLine("RES " + Hotels.Count()); 
    Console.WriteLine("RES " + Hotels.Count()); 
} 

打印

RES 1 
RES 1 
RES 1 

正如預期的那樣,你是如何填充枚舉和什麼類型的,你創造它?

如果您通過未執行的查詢來填充,您可能會得到像這樣的奇怪行爲,因爲它會選擇第一個用法(即在這種情況下爲First())。

+0

我用LINQ填充它 – markzzz 2012-03-12 09:42:49

+0

你可以發佈你的linq語句嗎?也是直接從數據庫中獲得這個嗎? – 2012-03-12 09:43:25

+0

查看更新後的問題! – markzzz 2012-03-12 09:44:09

1

「第一個」方法可能會使迭代器指向第二個元素。你第一次調用「計數」然後完成對你的一個元素序列的迭代,隨後的計數調用重新開始迭代。 Count總是迭代整個序列,所以它是冪等的。您需要一種方法來重置枚舉數Hotels.Reset()First()使Count()行爲正確。

最簡單的事情可能是自己做一個Reset擴展,但我不能重現您的問題。如果你有一些系統生成的數據庫迭代器,它可能不會自動重置,你可以使用下面的代碼。它只是利用你自己的發現,一個Count()重置迭代器。這將是O(n)來完成。

static class Extensions 
{ 
    public static void Reset<T>(this IEnumerable<T> toReset) 
    { 
     if (toReset != null) 
     { 
      int i = toReset.Count(); 
     } 
    } 
} 

但我無法重現你的問題:下面

代碼給整個正確的結果。你有沒有給你寫過自己的統計員或收藏?

static void Main(string[] args) 
{ 
    var Response = System.Console.Out; 

    var Hotels = new[]{1, 2, 3, 4}; 
    var Hotel = 0; 

    Response.Write("RES " + Hotels.Count() + "<br />"); 
    if (Hotels.Count() > 0) 
    { 
     Hotel = Hotels.First(); 
    } 
    Response.Write("RES " + Hotels.Count() + "<br />"); 
    Response.Write("RES " + Hotels.Count() + "<br />"); 

    Console.WriteLine("Hotel: " + Hotel); 
} 
+0

IEnumerable上沒有.Reset()方法,已經檢查:( – markzzz 2012-03-12 09:38:59

+2

不,它在枚舉器上。您在'Hotels'中使用哪個集合? - 我無法使用數組重現您的錯誤。 – faester 2012-03-12 09:40:36

+0

markzzz,我想你使用一些奇怪的對象作爲你的IEnumerable 。可能是你自己的實現,弄亂了事情 – 2012-03-12 09:45:44

3

這裏沒有魔法。看起來像Hotels集合中的項目數量會隨着時間的推移而變化,也許一些LINQ查詢和執行的執行,甚至可能是LINQ到SQL。

請顯示填充Hotels的完整代碼。

1

根據底層實現,重新枚舉IEnumerable不能以任何方式保證返回相同的值。爲了明白爲什麼,考慮這個例子;

static void Main(string[] args) 
{ 
    IEnumerable<int> bop = RandomSequence(); 
    Console.WriteLine(bop.Count()); 
    Console.WriteLine(bop.Count()); 
} 

private static int _seed = 0; 
static IEnumerable<int> RandomSequence() 
{ 
    var random = new Random(_seed++); 
    int randomNumber; 
    while ((randomNumber = random.Next(100)) != 0) 
     yield return randomNumber; 
} 

這是一個完全有效的IEnumerable,並兩次調用COUNT()將評估兩個不同的僞隨機值。原因是Count()重新枚舉相同的IEnumerable並生成完全不同的隨機序列。

如果你想要可重複的枚舉,你需要在enumerable上調用ToList()ToArray來存儲結果,並且每次都從List /數組中枚舉所有枚舉。