В исходный код Dictionary<TKey, TValue>
было внесено изменение, позволяющее обновлять существующие ключи во время перечисления. Его совершил 9 апреля 2020 года Стивен Туб. Эту фиксацию можно найти PR № 34667.
PR называется Разрешить перезапись словаря во время перечисления и отмечает, что он устраняет проблему № 34606. Рассмотрите возможность удаления _version++
от перезаписи в Dictionary<TKey, TValue>
. Текст этого номера, открытого г-ном Тубом, выглядит следующим образом:
Ранее мы удалили _version++
при удалении из словаря. Мы должны подумать об этом и при простой перезаписи значения для существующего ключа в словаре. Это позволит включить циклы обновления, которые изменяют значение в словаре, без необходимости прибегать к запутанным и более дорогостоящим методам.
В комментариях к этому вопросу спрашивается:
Какая польза от этого?
На что Стивен Туб ответил:
Как указано в исходном посте, тонкие шаблоны, которые в настоящее время бросают сегодня, начнут работать правильно, например
foreach (KeyValuePair<string, int> pair in dict)
dict[pair.Key] = pair.Value + 1;
Если вы посмотрите на Dictionary<, >
исходный код, вы можете видеть, что поле _version
(которое используется для обнаружения изменений) теперь обновляется только при определенных условиях, а нет при изменении существующего ключа.
Особый интерес представляет область _ 9_ (который вызывается индексатором, см. Ниже) и его третий параметр типа InsertionBehavior
. Если это значение равно InsertionBehavior.OverwriteExisting
, поле управления версиями не обновляется для существующего ключа.
Например, см. Этот раздел кода из обновленного TryInsert
:
if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
return true;
}
До изменения этот раздел выглядел следующим образом (мой комментарий кода):
if (behavior == InsertionBehavior.OverwriteExisting)
{
entries[i].value = value;
_version++; // <-----
return true;
}
Обратите внимание, что приращение поля _version
было удалено, что позволяет вносить изменения во время перечисления.
Для полноты, установщик индексатора выглядит так. Он не был изменен этим изменением, но обратите внимание на третий параметр, который влияет на приведенное выше поведение:
set
{
bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting);
Debug.Assert(modified);
}
Remove
из словаря больше не влияет на перечисление. Это, однако, существует с netcore 3.0 и соответствующим образом вызывается в документации Remove
:
Только .NET Core 3.0+: этот изменяющий метод можно безопасно вызывать без недействительности активных перечислителей в экземпляре Dictionary<TKey,TValue>
. Это не подразумевает потокобезопасность.
Несмотря на то, что один разработчик настаивал на решении связанной проблемы, чтобы документация была обновлена (и что, похоже, является гарантией того, что это будет), документы для индексатора еще не были (2021-04-04) обновлено, чтобы отразить текущее поведение.
person
pinkfloydx33
schedule
04.04.2021
Dictionary<, >
, начав чтение сset
средства доступа индексатораpublic TValue this[TKey key] { /* ... */ }
. Должно существовать личное поле, которое отслеживает, была ли изменена коллекция или нет. Если я назначаю с помощью установщикаd[k] = tmp;
иk
ранее был вDictionary<,>
, считается ли это модификацией? Должно быть, они это изменили. - person Jeppe Stig Nielsen   schedule 04.04.2021entries[i].value = value; version++; return;
в ветке где у нас обновление.version++
гарантирует, что счетчик взорвется с исключением на следующемMoveNext()
в счетчике. - person Jeppe Stig Nielsen   schedule 04.04.2021foreach (var v in d.Values) { Console.WriteLine("Top " + v + " " + d["c"]); d["c"] += 1; Console.WriteLine("Bottom " + v + " " + d["c"]); }
. Так что я думаю, что есть только одно полеversion
для ключей и значений. Где я могу увидеть новый исходный код C # дляpublic class Dictionary<TKey, TValue>
?) - person Jeppe Stig Nielsen   schedule 04.04.2021