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

10

我有一段代码,会在游戏每一帧运行:

    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个回答

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

你还需要修改代码,以便在键(key)更改时实际上从字典中删除一个条目并添加新的条目,因为键实际上不能被更改,只能被删除。
再次强调,这并不是推荐的做法。


3
不行,你不能这样做。有一种非常糟糕的选项可以使用:
public class Wrapper<T>
{
    public T WrappedValue { get; set; }

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

然后你会创建(比如说)一个Dictionary<string, WrappedValue<int>>,遍历键/值对,并改变包装器中的值,而不是使条目本身引用不同的包装器。 我认为我不建议这样做——它会很麻烦使用,容易被误用。 如果你使用的是.NET 4,则另一种选择是使用ConcurrentDictionary,它允许并发修改。

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

抱歉,你不能这样做。

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


0

你应该使用另一种模式来完成你想要做的事情,因为foreach不允许你更改正在循环遍历的枚举器。想象一下,如果你从开头在一个已排序的列表上运行foreach,你开始处理key="A"的项,然后转到"B",然后你将"C"更改为"B",会发生什么?你的列表被重新排序了,你不知道你正在循环和你在哪里。

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


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接