Методы словаря 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>
это бросить, когда словарь изменен во время перечисления.
Демо:
- .NET Framework
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 (выкидывает).
Чтобы уточнить, когда было внесено изменение и какими методами...
Документы компании Microsoft по словарю в .remove и .clear были обновлены:
Только.NET Core 3.0+: этот изменяющий метод можно безопасно вызывать без недействительности активных перечислителей в экземпляре Dictionary
. Это не подразумевает потокобезопасность.
.NET Core 3.0 поставляется с C# 8.0. С тех пор мы смогли изменить Dictionaryforeach
) Через .remove и .clear только.