You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改Dictionary键并迭代时触发集合修改异常,如何解决?

解决字典枚举时修改集合的异常问题

首先,咱们先搞清楚你遇到的The Collection has been modified enumeration operator may not execute异常的根本原因:当你用foreach枚举字典的Keys(或Values、直接枚举字典)时,.NET的枚举器会锁定集合的状态,不允许在枚举过程中添加/删除元素——因为枚举器是基于当前集合的快照工作的,一旦集合被修改,枚举器就失效了,自然会抛出这个异常。

看你的代码,问题出在这段循环逻辑里:

foreach (var container in SuggestedDictionary.Keys) {
    rec.Add(new Recommendations() { bookName = container, Rate = CalculatePearsonCorrelation(bkName, container) });
}
SuggestedDictionary.Remove(SuggestedDictionary.Keys.ToString());
if (!SuggestedDictionary.ContainsKey(entry[i].bookName)) {
    SuggestedDictionary.Add(entry[i].bookName, list);
}

你在遍历SuggestedDictionary.Keys的同一个迭代周期内,紧接着就对原字典执行了Remove和Add操作——哪怕修改操作不在foreach循环内部,只要枚举器还在生效期间修改了集合,就会触发异常。

直接修复方案:遍历键的副本

最简单的修复方式是把要遍历的键先复制到一个独立的列表里,这样遍历的是副本,原字典的修改不会影响枚举过程。把遍历Keys的代码改成这样:

// 把Keys转成List,遍历副本而非原集合
foreach (var container in SuggestedDictionary.Keys.ToList()) {
    rec.Add(new Recommendations() { bookName = container, Rate = CalculatePearsonCorrelation(bkName, container) });
}

顺便修正代码里的其他潜在问题

除了枚举异常,你的代码还有几个需要注意的点:

  1. 填充列表的无限循环风险:

    List<Entity> list = db.books.ToList();
    foreach(var l in list) { list.Add(l); }
    

    这行代码会导致无限循环——因为你在遍历list的同时往里面添加元素,list的长度会一直增加,永远遍历不完。其实db.books.ToList()已经把所有书籍数据取到list里了,后面的foreach完全多余,直接删掉就行。

  2. 错误的键移除方式:

    SuggestedDictionary.Remove(SuggestedDictionary.Keys.ToString());
    

    SuggestedDictionary.Keys是一个键集合,ToString()得到的是集合的字符串表示(比如System.Collections.Generic.Dictionary2+KeyCollection`),根本不是你要移除的键。如果你是想移除当前字典里的唯一键(因为你初始只加了一个"Key"),可以改成:

    if (SuggestedDictionary.Any()) {
        var oldKey = SuggestedDictionary.Keys.First();
        SuggestedDictionary.Remove(oldKey);
    }
    

    这样能安全地移除第一个键,还避免了字典为空时的报错。

更优的实现思路

如果你的核心需求是反复替换字典的键,然后基于当前键计算推荐,其实没必要频繁修改字典——字典的优势是快速查找,但如果每次只保留一个键,直接用一个变量存储当前键会更高效:

// 用变量代替字典存储当前键,避免频繁修改字典
string currentKey = "Key";
List<Entity> currentList = db.books.ToList();

foreach (var entry in CopyDataDict.Values.ToList()) {
    for (int i = 2; i < 15; i++) {
        // 直接用currentKey计算推荐
        rec.Add(new Recommendations() { bookName = currentKey, Rate = CalculatePearsonCorrelation(bkName, currentKey) });
        
        // 替换当前键
        currentKey = entry[i].bookName;
        // 如果需要保留列表关联,也可以用字典,但只维护单个键的话变量更高效
    }
}

如果确实需要维护字典(比如后续可能有多个键),可以考虑用ConcurrentDictionary(线程安全字典),它支持在枚举时修改集合,但如果你的场景不是多线程,用前面的ToList()复制副本的方案就足够了。

内容的提问来源于stack exchange,提问作者Muhammad Shahid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:00:14