2011-02-23 106 views
9

我有這樣的代碼正在被運行遊戲的每一幀:C# - 字典鍵值對的變化值,而在的foreach

foreach (var repeaterAction in conditionTimes.Keys) 
    { 
     if (repeaterAction.Condition() == true) 
     { 
      if (conditionTimes[repeaterAction] == TimeSpan.Zero) 
      { 
       repeaterAction.Action(); 
      } 
      else if (conditionTimes[repeaterAction] >= repeaterAction.InitialLapse) 
      { 
       repeaterAction.Action(); 
       conditionTimes[repeaterAction] -= repeaterAction.ActionInterval; 
      } 
      conditionTimes[repeaterAction] += gameTime.ElapsedGameTime; 
     } 
     else 
     { 
      conditionTimes[repeaterAction] = TimeSpan.Zero; 
     } 
    } 

這是給我下面的錯誤:

Collection was modified; enumeration operation may not execute.

有沒有辦法修改foreach循環內鍵值對中的值,而不需要每一幀都複製字典?

回答

0

對不起,你不能。

我想你最好的選擇是創建一個新的字典,然後將它與舊的字典交換,當你完成foreach循環。

0

你應該使用另一種模式來做你正在做的事情,因爲每一個都不允許你改變你循環到的枚舉器。想象一下,如果你從一開始就在一個排序列表上運行一個foreach,你開始用key =「A」處理項目,然後你到「B」,那麼你將「C」改爲「B」,會發生什麼?你的名單被採取,你不知道你在循環和你在哪裏。

一般來說,你可以用for(int i = dictionary.count-1; i> = 0; --i)來做到這一點,但這也取決於你的上下文,我真的會嘗試使用另一種方法。

0

在枚舉foreach語句時,您無法修改集合。所以你必須每次都複製它(你只需要複製密鑰)。另一種選擇是隨時存儲一個單獨的密鑰列表(例如,通過將字典封裝在管理此類的類中)。是這樣的:

class MyDictionary<TKey, TValue> 
{ 
    private Dictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>(); 
    private List<Keys> _keys = new List<TKey>(); 

    public void Add(TKey key, TValue value) 
    { 
     _dict.Add(key, value); 
     _keys.Add(key); 
    } 

    //public bool Remove ... 
    //indexer... 
} 

當然,在並行環境中,你就必須確保字典和列表的同步...

0

一種常見的方法是記住要更改鍵第一個循環,然後第二個循環遍歷記憶的鍵並更改原始字典。這避免了創建一個包含所有元素的全新字典。

3

不,你不能。還有一個討厭選項,你可以使用:

public class Wrapper<T> 
{ 
    public T WrappedValue { get; set; } 

    // *Maybe* add implicit conversions here? Icky... 
} 

然後你會內包裝而不是創造(比如說)Dictionary<string, WrappedValue<int>> ...遍歷鍵/值對,改變價值使條目本身引用不同的包裝。

我不認爲我會推薦這個,雖然 - 它會使用起來很尷尬,並容易誤用

如果您使用.NET 4,另一種選擇是使用ConcurrentDictionary,其中確實允許允許併發修改。

13

我建議不要嘗試修改集合,同時通過字典循環訪問,但是由於可以使用直接鍵訪問,因此可能會發生這種情況。只需在foreach那麼鍵conditionTimes.Keys之後添加.ToArray()的單獨收集,你可以修改詞典:

foreach (var repeaterAction in conditionTimes.Keys.ToArray()) 
{ 
    if (repeaterAction.Condition() == true) 
    { 
     if (conditionTimes[repeaterAction] == TimeSpan.Zero) 
     { 
      repeaterAction.Action(); 
     } 
     else if (conditionTimes[repeaterAction] >= repeaterAction.InitialLapse) 
     { 
      repeaterAction.Action(); 
      conditionTimes[repeaterAction] -= repeaterAction.ActionInterval; 
     } 
     conditionTimes[repeaterAction] += gameTime.ElapsedGameTime; 
    } 
    else 
    { 
     conditionTimes[repeaterAction] = TimeSpan.Zero; 
    } 
} 

您還可以修改你的代碼,這樣,如果在關鍵的變化,你居然刪除條目從你的字典中添加新的,因爲鍵真的不能改變,只是刪除。
這也不建議。

+0

這太棒了!謝謝你已經度過了我的一天 – zabulus 2012-05-14 13:49:33