首页 > 解决方案 > 字典方法 Remove 和 Clear (.NET Core) 在枚举期间修改集合。没有抛出异常

问题描述

我正在尝试实现一种缓存机制来安全地枚举集合,并且我正在检查内置集合的所有修改是否都触发了InvalidOperationException它们各自的枚举器抛出的问题。我注意到在 .NET Core 平台中,Dictionary.RemoveandDictionary.Clear方法没有触发这个异常。这是错误还是功能?

示例Remove

var dictionary = new Dictionary<int, string>();
dictionary.Add(1, "Hello");
dictionary.Add(2, "World");
foreach (var entry in dictionary)
{
    var removed = dictionary.Remove(entry.Key);
    Console.WriteLine($"{entry} removed: {removed}");
}
Console.WriteLine($"Count: {dictionary.Count}");

输出:

[1, Hello] 已移除:True
[2, World] 已移除:True
计数:0

示例Clear

var dictionary = new Dictionary<int, string>();
dictionary.Add(1, "Hello");
dictionary.Add(2, "World");
foreach (var entry in dictionary)
{
    Console.WriteLine(entry);
    dictionary.Clear();
}
Console.WriteLine($"Count: {dictionary.Count}");

输出:

[1,你好]
计数:0

预期的例外是:

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

...由 method 引发Add,并由 .NET Framework 中的相同方法引发。

.NET Core 3.0.0、C# 8、VS 2019 16.3.1、Windows 10

标签: c#dictionaryfor-loop.net-coreenumeration

解决方案


这似乎是 .Net 完整框架和 .Net 核心之间的故意区别Dictionary<TKey, TValue>

分歧发生在Pull #18854: Remove version increment from Dictionary.Remove 重载

从 Remove 操作中删除版本增量

这解决了 api 更改Add Dictionary.Remove(predicate)的 coreclr 方面,目的是允许从字典中删除项目,同时从 @jkotas 枚举每个方向。在相关的 corefx PR 中添加了所有集合测试以及修改和新的测试。

似乎有一个开放的文档问题:

问题#42123:澄清枚举期间围绕突变的字典行为/保证

说字典的当前实现在迭代期间支持非并发突变是否正确?

只有拆除。这是dotnet/coreclr#18854中的一项功能。

这是可以依靠的东西吗?

是的。

我们应该确保更新文档以反映这一点。

您可能想要对请求澄清的开放文档问题添加投票,因为.Net core 3.0 文档Dictionary<TKey,TValue>.GetEnumerator()现已过时

如果对集合进行了更改,例如添加、修改或删除元素,则枚举器将不可恢复地失效,并且下一次调用MoveNextIEnumerator.Reset抛出InvalidOperationException.

奇怪的是,在枚举期间修改字典时,枚举器确实会抛出。SortedDictionary<TKey, TValue>

演示:

  • .Net 框架Remove()https ://dotnetfiddle.net/8vONOw (抛出)。
  • .Net 核心Remove()https ://dotnetfiddle.net/es6STm (不抛出)。
  • .Net 核心Add()https ://dotnetfiddle.net/6q7Lvx (抛出)。
  • .Net 核心Remove()来自SortedDictionary<int, string>https ://dotnetfiddle.net/bssrG7 (抛出)。

推荐阅读