会在foreach中从Dictionary中删除一个键导致问题吗?或者我应该更好地构建一个新的词典?

col*_*ang 16 c#

例如:

1.

         foreach (var item in myDic)
                {
                  if (item.value == 42)
                        myDic.remove(item.key);
                }
Run Code Online (Sandbox Code Playgroud)

无论内括号中的语句如何影响,迭代器都能正常工作myDic吗?

2.

    var newDic = myDic.where(x=>x.value!=42).ToDictionary(x=>x.key,x=>x.value);
Run Code Online (Sandbox Code Playgroud)

第二种方法是一种好的做法吗?函数式编程和不可变?

mqp*_*mqp 29

第一种方法将在运行时崩溃,因为枚举器确保在枚举时没有人从底层集合中删除.

第二种方法是一个很好的想法,但C#字典是可变的,如果你可以用变异完成同样的事情,那么复制它们既不惯用也不高效.

这是一种典型的方式:

var itemsToRemove = myDic.Where(f => f.Value == 42).ToArray();
foreach (var item in itemsToRemove)
    myDic.Remove(item.Key);
Run Code Online (Sandbox Code Playgroud)

编辑:在评论中回答您的问题.以下是您的其他问题中的示例:

myList = myList.where(x=>x>10).select(x=>x-10);
Run Code Online (Sandbox Code Playgroud)

这行代码没有运行任何东西; 它完全是懒惰的.让我们说,为了论证,我们有一个foreach后来使它看起来更像这个问题的例子.

foreach (int n in myList)
    Console.WriteLine(n);
Run Code Online (Sandbox Code Playgroud)

执行时,这是每次迭代时会发生的事情:

  1. 呼吁MoveNext调查员
  2. 枚举器查找下一个大于十的值
  3. 然后它取该值减去十并将Current属性设置为该值
  4. Current属性绑定到变量n
  5. Console.WriteLine

你可以看到没有神秘感,也没有无限循环而没有任何东西.

现在比较我的例子,假设我们遗漏了ToArray.

var itemsToRemove = myDic.Where(f => f.Value == 42);
foreach (var item in itemsToRemove)
    myDic.Remove(item.Key);
Run Code Online (Sandbox Code Playgroud)
  1. 呼吁MoveNext调查员
  2. 枚举器找到值为42的下一对,并将Current属性设置为该值
  3. Current属性绑定到变量item
  4. Remove

这不起作用,因为虽然WriteLine当你打开一个枚举器时它对集合中的某些内容完全没问题,但是当你打开一个枚举器时,你不允许Remove从集合中获取某些内容.

如果你ToArray在前面调用,那么你首先要枚举字典并填充数组.当我们到达时foreach,该foreach语句在数组上打开一个枚举,而不是字典.在迭代数组时,您可以从字典中删除.

  • 不,你不能省略它.如果你把它保持懒惰,那么当调用`Remove`时它仍然会在字典上进行枚举,并且你会在你开始的地方回来(在运行时有例外).你需要做所有工作来构建在实际开始删除之前要删除的项目列表. (3认同)

Joh*_*ohn 13

根据文档,从 .NET Core 3.0 开始,删除元素将不再影响活动枚举器。您可以在迭代时安全地删除项目:

foreach (var item in myDic)
{
    if (item.Value == 42)
        myDic.Remove(item.Key);
}
Run Code Online (Sandbox Code Playgroud)

Dictionary<TKey,TValue>.Remove 方法

仅限 .NET Core 3.0+:可以安全地调用此变异方法,而无需使 Dictionary<TKey,TValue> 实例上的活动枚举器失效。这并不意味着线程安全。


Vla*_*lav 7

您还可以遍历集合的副本:

foreach (var item in myDic.ToList())
{
    if (item.value == 42)
    myDic.remove(item.key);
}
Run Code Online (Sandbox Code Playgroud)

声明myDic.ToList()中的通知foreach.