C# - 在foreach中更改字典键值对的值

时间:2011-02-23 08:50:17

标签: c# collections dictionary foreach enumeration

我有这个代码正在游戏的每一帧运行:

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

这给了我以下错误:

  

收藏被修改;枚举操作可能无法执行。

有没有办法修改foreach循环中键值对中的值,而不是每帧都复制Dictionary?

6 个答案:

答案 0 :(得分:13)

我建议不要尝试修改集合,同时使用字典循环它,但是可以使用直接密钥访问。只需在.ToArray()conditionTimes.Keys之后添加foreach,然后密钥就会成为一个单独的集合,您可以修改字典:

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

你还必须修改你的代码,这样如果密钥改变你实际上从字典中删除一个条目并添加新条目,因为密钥真的无法更改,只需删除。
同样不建议这样做。

答案 1 :(得分:3)

不,你不能。你可以使用一个讨厌的选项:

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

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

然后你创建(比方说)一个Dictionary<string, WrappedValue<int>> ...遍历键/值对,并在包装器中更改值而不是让条目本身引用一个不同的包装。

我不认为我会推荐这个 - 使用它会很尴尬,并且容易滥用

如果您使用.NET 4,另一个选择是使用ConcurrentDictionary 允许并发修改。

答案 2 :(得分:0)

对不起,你不能。

我猜你最好的办法是创建一个新词典,然后在完成foreach循环后将其与旧词典交换。

答案 3 :(得分:0)

您应该使用其他模式来执行您尝试执行的操作,因为for each不允许您更改要循环到的枚举器。想象一下,如果你从一开始就在一个排序列表上运行foreach,你开始用key =“A”处理项目然后你转到“B”然后你将“C”改为“B”,会发生什么?你的名单已经使用了,你不再知道你在循环什么以及你在哪里。

一般来说你“可以”用for(int i = dictionary.count-1; i&gt; = 0; --i)或类似的东西来做它但这也取决于你的上下文,我真的会尝试使用另一种方法。

答案 4 :(得分: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...
}

当然,在并发环境中,您必须确保字典和列表已同步......

答案 5 :(得分:0)

一种常见的方法是记住要在第一个循环中更改的键,然后让第二个循环遍历记住的键并更改原始字典。这避免了创建一个包含所有元素的全新字典。