2014-03-31 29 views
2

我知道foreach内不修改收集的基本原则,这就是为什么我做了这样的事情:异常修改集合中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异常,然后我通过它。但我仍然得到了我无法理解的例外。

如何更正此问题以及导致问题的原因?

回答

3

首先保存我的所有键

不,你不知道; keys是一个活动序列,它正在积极迭代该集合,因为它由foreach迭代。创建密钥的副本孤立,你需要添加.ToList()(或类似)到最后:

var keys = coverages.Select(pair => pair.Key).ToList(); 

虽然我个人很可能去:

var keys = new PlayerID[coverages.Count]; 
coverages.Keys.CopyTo(keys, 0); 

(它允许正确-length分配和存储器复制)


什么是实况序列实际?当你第一次启动迭代var key in keys,它抓住的coverages序列:

Select方法对另一...这是一个真正复杂东西理解,但基本上创建非缓冲假脱机迭代器(又名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! 
+0

什么是实际的实时序列? –

+0

@Michał复杂...我会编辑 –