【问题标题】:Why am I getting "Collection was modified; enumeration operation may not execute" when not modifying the enumerated collection? [duplicate]为什么不修改枚举集合时出现“集合已修改;枚举操作可能无法执行”? [复制]
【发布时间】:2010-05-07 20:31:51
【问题描述】:

我有两个字符串集合:CollectionA 是存储在系统中的对象的 StringCollection 属性,而 CollectionB 是在运行时生成的 List。如果有任何差异,则需要更新 CollectionA 以匹配 CollectionB。所以我设计了一个我期望的简单的 LINQ 方法来执行删除。

var strDifferences = CollectionA.Where(foo => !CollectionB.Contains(foo));
foreach (var strVar in strDifferences) { CollectionA.Remove(strVar); }

但我在 strDifferences 上收到 "Collection was modified; enumeration operation may not execute" 错误...即使它是与正在修改的集合分开的可枚举!我最初设计这个明确是为了规避这个错误,因为我的第一个实现会产生它(因为我在CollectionA 中枚举并在!CollectionB.Contains(str) 时删除)。任何人都可以深入了解为什么此枚举失败?

【问题讨论】:

    标签: c# linq enumeration


    【解决方案1】:

    两者并不完全分开。 Where 不会制作集合的单独副本。它在内部保留对原始集合的引用,并在您请求元素时从中获取元素。

    您可以通过添加ToList() 来强制Where 立即遍历集合来解决您的问题。

    var strDifferences = CollectionA
        .Where(foo => !CollectionB.Contains(foo))
        .ToList();
    

    【讨论】:

    • ToArray() 在这里会不会更好?无需使用List
    • @svick 这是我很长时间以来一直想知道的问题,但这是我改天再问的问题。如果还没有被问到,当然-chuckle-
    • @GraceNote 在这里捕捉到:stackoverflow.com/questions/1105990/…
    【解决方案2】:

    Where 传回IEnumerable<T> 然后你在你的foreach (var strVar in strDifferences) 中使用它

    然后您尝试将其从创建 IEnumerable<T> 的集合中删除。您尚未创建新列表,它引用 CollectionA 以从中提取下一项,因此您无法编辑 CollectionA

    你也可以这样做:

    var strDifferences = CollectionA.Where
      (foo => CollectionB.Contains(foo)).ToList();
    
    CollectionA = strDifferences;
    //or instead of reassigning CollectionA
    CollectionA.Clear();
    CollectionA.AddRange(strDifferences);
    

    因为您要删除不在 CollectionB 中的那些。只需查找那些,创建一个列表并将该列表分配给 CollectionA 变量。

    【讨论】:

    • 啊...我很想使用您的替代方案(但实际上它是相同的),但不幸的是,事实证明 CollectionA 是一个只读属性,所以我无法设置它。不过,您确实给出了更深入的解释,因此为此 +1。
    • @ccornet 如果无法设置,您可以清除它并重新添加项目。
    • 我想了很久。我决定仍然采用删除方法。如果不需要更改,我不希望修改 CollectionA,构建需要删除的列表可以让这非常快速且易于了解(只需查看 strDifferences.Count > 0)。如果我构建一个目标数组,我仍然需要检查 CollectionA 是否有需要删除的东西。
    【解决方案3】:

    尝试稍微修改第一行:

    var strDifferences =
        CollectionA.Where(foo => !CollectionB.Contains(foo)).ToList();
    

    我认为 LINQ 正在使用您的查询的延迟执行。对ToList 的调用将在枚举之前强制执行查询。

    【讨论】:

    • 是的,并确保不要把它放在两行上,即var strDifferencesTemp = CollectionA.Where(foo => !CollectionB.Contains(foo)); var strDifferences = strDifferencesTemp.ToList();如果这样做,则可以通过这两行之间的其他线程修改集合。
    • @Markus 如果是多线程,即使你把它放在一行中也没有任何区别。在这种情况下,您最好考虑 immutablelist。
    猜你喜欢
    • 2015-06-17
    • 2020-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-17
    • 2011-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多