3

这似乎应该得到回答,但我发现的潜在骗子在问不同的事情......

我注意到这似乎工作正常(sourceDirInclusion很简单Dictionary<X,Y>

    foreach (string dir in sourceDirInclusion.Keys)
    {
        if (sourceDirInclusion[dir] == null)
            sourceDirInclusion.Remove(dir);
    }

这是否意味着从集合中删除项目foreach是安全的,或者我很幸运?

如果我向字典中添加更多元素而不是删除元素呢?

我要解决的问题sourceDirInclusion最初已填充,但随后每个值都可以在第二遍中将新项目贡献给字典。例如,我想做的是:

foreach (string dir in sourceDirInclusion.Keys)
{
  X x = sourceDirInclusion[dir];
  sourceDirInclusion.Add(X.dir,X.val);
}
4

4 回答 4

7

简短的回答:这不安全。

长答案:来自IEnumerator<T>文档

只要集合保持不变,枚举数就保持有效。如果对集合进行了更改,例如添加、修改或删除元素,则枚举器将不可恢复地失效,并且其行为未定义。

请注意,文档说该行为未定义,这意味着它可能有效,也可能无效。永远不应该依赖未定义的行为。

在这种情况下,它取决于可枚举对象的行为,即Keys在您开始枚举时它是否创建键列表的副本。在这种特定情况下,我们从文档中知道返回值Dictionary<,>.Keys是一个引用字典的集合:

返回Dictionary<TKey, TValue>.KeyCollection的不是静态副本;相反, theDictionary<TKey, TValue>.KeyCollection引用了原始Dictionary<TKey, TValue>. 因此,变化Dictionary<TKey, TValue>继续体现在Dictionary<TKey, TValue>.KeyCollection.

所以在枚举字典的键时修改字典应该被认为是不安全的。

您可以通过一次更改来纠正此问题。改变这一行:

foreach (string dir in sourceDirInclusion.Keys)

对此:

foreach (string dir in sourceDirInclusion.Keys.ToList())

扩展方法将ToList()创建键列表的显式副本,从而可以安全地修改字典;“基础集合”将是副本而不是原件。

于 2013-01-24T17:40:58.687 回答
3

如果会抛出

InvalidOperationException: Message="集合已修改;枚举操作可能无法执行

为了避免这种情况,将要删除的候选人添加到外部列表中。然后循环它并从目标容器(字典)中删除。

List<string> list = new List<string>(sourceDirInclusion.Keys.Count);
foreach (string dir in sourceDirInclusion.Keys)
{
    if (sourceDirInclusion[dir] == null)
        list.Add(dir);
}
foreach (string dir in list)
{
    sourceDirInclusion.Remove(dir);
}
于 2013-01-24T17:40:37.097 回答
0

这是有效的,因为您正在遍历 sourceDirInclusion.Keys。

但是,为了确保 FrameWork 的未来版本,我建议您在 foreach 语句中使用 sourceDirInclusion.Keys.ToArray() ,这样您将创建循环遍历的键的副本。

然而,这将不起作用:

foreach(KeyValuePair<string, object> item in sourceDirInclusion)
{
    if (item.Value == null)
        sourceDirInclusion.Remove(item.Key);
}

通常,您不能在遍历集合时对其进行修改,但通常您可以使用 .ToArray() 或 .ToList() 创建一个新集合,并在修改原始集合时对其进行遍历。

祝你的任务好运。

于 2013-01-24T17:44:38.940 回答
0

检查一下:在“foreach”循环中修改列表的最佳方法是什么?

简而言之:

The collection used in foreach is immutable. This is very much by design.

正如 MSDN 上所说:

foreach 语句用于遍历集合以获取您想要的信息,但不能用于从源集合中添加或删除项目以避免不可预知的副作用。如果您需要从源集合中添加或删除项目,请使用 for 循环。

更新: 您可以改用 for 循环:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
于 2013-01-24T17:40:44.577 回答