开发者

C# - Change value of dictionary key-value pair while in foreach

I have this code which is being run every frame of a game:

    foreach (var repeaterAction in conditionTimes.Keys)
    {
        if (repeaterAction.Condition() == true)
        {
            if (conditionTimes[repeaterAct开发者_JAVA百科ion] == 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;
        }
    }

This is giving me the following error:

Collection was modified; enumeration operation may not execute.

Is there a way to modify the value in the key-value pair inside the foreach loop, without copying the Dictionary every frame?


I'm Advising against trying to modify a collection while looping through it with dictionaries however it is possible since direct key access is available. Just add .ToArray() after the conditionTimes.Keys in the foreach then the keys becomes a separate collection and you can modify the dictionary:

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;
    }
}

You also have to modify you're code so that if the key changes you actually remove an entry from your dictionary and add the new one, because keys really cant be changed, just removed.
Again this is not recommended.


No, you can't. There's one nasty option which you could use:

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

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

Then you'd create (say) a Dictionary<string, WrappedValue<int>>... iterate over the key/value pairs, and change the value within the wrapper rather than making the entry itself refer to a different wrapper.

I don't think I'd recommend this though - it would be awkward to use, and easy to misuse.

Another option if you're using .NET 4 is to use ConcurrentDictionary, which does allow concurrent modifications.


Sorry, you can't.

I guess your best bet is to create a new Dictionary then swap it with the old one when you're done with the foreach loop.


you should use another pattern to do what you are trying to do because the for each does not allow you to change the enumerator you are looping to. Imagine if you run a foreach on a sorted list from the beginning, you start processing item with key="A" then you go to "B" then you change "C" to "B", what's going to happen? Your list is resorted and you don't know anymore what you are looping and where you are.

in general you "could" do it with a for(int i=dictionary.count-1; i>=0; --i) or something like that but this also depends on your context, I would really try to use another approach.


A common approach is to remember the keys you want to change in the first loop, and then have a second that loops over the remembered keys and changes the original dictionary. That avoids creating a completely new dictionary with all elements.


You can't modify a collection while enumerating it inside a foreach statement. So you'd either have to copy it each time (you only need to copy the Keys). Another option would be storing a separate key list at all times (for example, by encapsulating the dictionary inside a class that manages this). something like:

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...
}

Of course, in a concurrent environment you'd have to make sure the dictionary and list are synced...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜