在 foreach 循环中修改集合时出现异常
本文关键字:异常 集合 修改 foreach 循环 | 更新日期: 2023-09-27 17:56:01
我知道不修改foreach
内集合的基本原理,这就是为什么我做了这样的事情:
public void UpdateCoverages(Dictionary<PlayerID, double> coverages)
{
// TODO: temp
var keys = coverages.Select(pair => pair.Key);
foreach (var key in keys)
{
coverages[key] = 0.84;
}
}
和:
class PlayerID : IEquatable<PlayerID>
{
public PlayerID(byte value)
{
Value = value;
}
public byte Value { get; private set; }
public bool Equals(PlayerID other)
{
return Value == other.Value;
}
}
首先,我保存所有密钥以免出现Collection modified
异常,然后通过它。但我仍然得到我无法理解的例外。
如何纠正此问题以及导致问题的原因?
首先我保存所有密钥
不,你没有; keys
是一个实时序列,它在foreach
迭代时主动迭代集合。若要创建密钥的独立副本,需要在末尾添加.ToList()
(或类似副本):
var keys = coverages.Select(pair => pair.Key).ToList();
虽然我个人可能会选择:
var keys = new PlayerID[coverages.Count];
coverages.Keys.CopyTo(keys, 0);
(允许正确长度分配和内存复制)
实际上什么是实时序列?
Select
方法在另一个方法上创建非缓冲假脱机迭代器...这是一件非常复杂的事情,但基本上:当你第一次开始迭代var key in keys
时,它会抓取coverages
的内部序列(又名coverages.GetEnumerator()
),然后每次foreach
请求下一项时,它都会要求下一项。是的,这听起来很复杂。好消息是C#编译器自动内置了所有内容,并为您生成状态机等。所有这些都主要使用 yield return
语法完成。Jon Skeet 在 C# 深入的第 6 章中对此进行了很好的讨论。IIRC这曾经是"免费章节",但现在不是了。
但是,请考虑以下事项:
static IEnumerable<int> OneTwoOneTwoForever()
{
while(true) {
yield return 1;
yield return 2;
}
}
您可能会惊讶地发现,您可以使用上述内容,使用相同的非缓冲"当您请求另一个值时,它运行足够的代码来为您提供下一个值"方法:
var firstTwenty = OneTwoOneTwoForever().Take(20).ToList(); // works!