2014-03-31 62 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ł複雜...我會編輯 –