2011-06-04 80 views
7

我還沒有使用Queues<T>之前的任何真正的程度,所以我可能會失去明顯的東西。我試圖通過Queue<EnemyUserControl>像這樣(每幀)進行迭代:Queue ForEach循環拋出InvalidOperationException

foreach (var e in qEnemy) 
{ 
    //enemy AI code 
} 

當敵人死亡時,敵人的用戶控件引發我訂閱了一個事件,我在做這個(第一個敵人隊列由設計刪除):

void Enemy_Killed(object sender, EventArgs e) 
{  
    qEnemy.Dequeue(); 

    //Added TrimExcess to check if the error was caused by NULL values in the Queue (it wasn't :)) 
    qEnemy.TrimExcess(); 
} 

然而,出列方法被調用後,我坐上foreach循環的InvalidOperationException。當我使用Peek代替時,沒有錯誤,所以它必須對Queue本身進行更改,因爲Dequeue刪除了該對象。 我最初的猜測是它抱怨說我正在修改由Enumerator迭代的集合,但是在循環之外執行出列?

任何想法可能會導致此問題?

謝謝

+1

您應該使用 '而(queue.Any())queue.Dequeue();' – Telemat 2015-02-14 09:45:35

回答

16

您正在修改foreach循環中的隊列。這是導致異常的原因。
簡化代碼來說明這個問題:

var queue = new Queue<int>(); 
queue.Enqueue(1); 
queue.Enqueue(2); 

foreach (var i in queue) 
{ 
    queue.Dequeue(); 
} 

可能的解決方案是增加ToList(),像這樣:

foreach (var i in queue.ToList()) 
{ 
    queue.Dequeue(); 
} 
+0

d '哦,是一個facepalm時刻。 AI代碼中的一種方法調用'Movement'方法,該方法反過來引發殺死的事件(我認爲它是由循環外部的代碼引發的),所以在循環內執行出列。 'ToList()'方法完美地工作。謝謝! – keyboardP 2011-06-04 01:46:00

1

這是普查員的典型行爲。大多數統計員只有在基礎集合保持靜態時才能正確運行。如果集合在列舉集合時發生更改,則塊將爲您注入的MoveNext的下一個調用將生成此異常。

Dequeue操作明顯改變了集合,這就是導致問題的原因。解決方法是將要從目標集合中移除的每個項目添加到第二個集合中。循環完成後,您可以循環執行第二個集合並從目標中移除。

但是,至少這可能有點尷尬,因爲Dequeue操作只會刪除下一個項目。您可能必須切換到允許任意刪除的不同集合類型。

如果您想堅持Queue,那麼您將被迫將每個項目出列並有條件地重新排列那些不應該被刪除的項目。您仍然需要第二個集合來跟蹤可以從重新排隊中省略的項目。

0

在遍歷它們不能從集合中刪除元素。

我發現的最佳解決方案是使用「列表<>刪除」,並添加任何你想刪除的列表。一旦foreach循環結束後,您可以刪除使用的toDelete列表中引用的目標集合中的元素,像這樣:

foreach (var e in toDelete) 
    target.Remove(e); 
toDelete.Clear(); 

現在,因爲這是一個隊列,你可能只是可以算的次數你希望以一個整數出隊並使用一個簡單的for循環來稍後執行它們(在這方面,我沒有那麼多隊列經驗)。

+0

您可以簡單地迭代隊列並清除它。在這種情況下,效果與使用List相同,所以使用Queue對此沒有意義(如果您不需要單獨出列)。 – arni 2014-12-11 09:29:48

0

無論你在哪裏修改集合。如果在枚舉其成員時修改了集合,則會發生異常。您可以使用鎖定,並確保在使用.NET 4.0替換QueueConcurrentQueue時,集合未被修改。

15

我知道這是一個老的文章,但怎麼樣以下幾點:

var queue = new Queue<int>(); 
queue.Enqueue(1); 
queue.Enqueue(2); 

do { 
    var val = queue.Dequeue(); 
} 
while (queue.Count > 0); 

乾杯

+4

我建議稍微修改一下這段時間,而不是do/while,以便在嘗試第一個.Dequeue()之前執行.Count檢查,以防隊列爲空。 – DaveD 2015-11-04 15:10:18