Методы словаря Remove и Clear (.NET Core) изменяют коллекцию во время перечисления. Нет исключения

Я пытаюсь реализовать механизм кэширования для безопасного перечисления коллекций, и я проверяю, все ли модификации встроенных коллекций запускаютInvalidOperationExceptionдолжны быть выброшены их соответствующими счетчиками. Я заметил, что на платформе.NET Core Dictionary.Remove а также Dictionary.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
Count: 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, Hello]
Количество: 0

Ожидаемое исключение:

InvalidOperationException: Коллекция была изменена; операция перечисления может не выполняться.

... как выбрасывается методом Add, и теми же методами в.NET Framework.

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

2 ответа

Решение

Это кажется преднамеренным различием между полной структурой.Net и ядром.Net для Dictionary<TKey, TValue>.

Расхождение произошло в запросе № 18854: удалить приращение версии из словаря. Удалить перегрузки:

Удаляет приращение версии из операций удаления

Это касается стороны coreclr изменения api Add Dictionary.Remove (предикат) с намерением разрешить удаление элементов из словаря при перечислении в каждом направлении из @jkotas. Все тесты коллекции, а также модифицированные и новые тесты добавлены в соответствующий PR corefx.

Похоже, есть открытая проблема с документацией:

Проблема № 42123: Уточнение поведения словаря / гарантии в отношении мутации во время перечисления:

Можно ли сказать, что текущая реализация Dictionary поддерживает непараллельное изменение во время итерации?

Только удаление. Это было включено как функция в dotnet / coreclr #18854.

это то, от чего можно зависеть от будущего

Да.

Мы должны убедиться, что документы обновлены, чтобы отразить это.

Возможно, вы захотите проголосовать за проблему с открытым документом с просьбой разъяснить, как в документации .Net core 3.0 дляDictionary<TKey,TValue>.GetEnumerator() в настоящее время устарели:

Если в коллекцию вносятся изменения, такие как добавление, изменение или удаление элементов, перечислитель становится безвозвратно недействительным и следующий вызовMoveNext или IEnumerator.Reset бросает InvalidOperationException.

Как ни странно, счетчик для SortedDictionary<TKey, TValue> это бросить, когда словарь изменен во время перечисления.

Демо:

Чтобы уточнить, когда было внесено изменение и какими методами...

Документы компании Microsoft по словарю в .remove и .clear были обновлены:

Только.NET Core 3.0+: этот изменяющий метод можно безопасно вызывать без недействительности активных перечислителей в экземпляре Dictionary. Это не подразумевает потокобезопасность.

.NET Core 3.0 поставляется с C# 8.0. С тех пор мы смогли изменить Dictionary во время перечисления (foreach) Через .remove и .clear только.

Другие вопросы по тегам