Причина в Словаре и ConcurrentDictionary имеет разные цели. ConcurrentDictionary - предполагается решать проблемы параллелизма (редактирование из разных потоков), в то время как словарь даст вам лучшую производительность.
Причиной различного поведения является: различная реализация метода GetEnumerator().
Теперь я объясню причину исключения в словаре и причину, по которой вы не получите исключение из ConcurrentDictionary.
Еогеасп заявление синтаксический сахар что-то вроде:
var f = dict.GetEnumerator();
while (f.MoveNext())
{
var x = f.Current;
// your logic
}
"GetEnumerator()" в Словаре возвращает новый экземпляр структуры под названием: "Enumerator"
Эта структура реализует: IEnumerator> KeyValuePair > TKey, TValue >>, IDictionaryEnumerator и его C'tor выглядит следующим образом:
internal Enumerator(Dictionary<TKey,TValue> dictionary, int getEnumeratorRetType) {
this.dictionary = dictionary;
version = dictionary.version;
index = 0;
this.getEnumeratorRetType = getEnumeratorRetType;
current = new KeyValuePair<TKey, TValue>();
}
реализация MoveNext() внутри "Enumerator" проверить первый что исходный словарь не был изменен:
bool moveNext(){
if (version != dictionary.version) {
throw new InvalidOperationException....
}
//the itarate over...
}
«GetEnumerator()» в ConcurrentDictionary реализован способ другое:
IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator(){
Node[] buckets = m_tables.m_buckets;
for (int i = 0; i < buckets.Length; i++)
{
Node current = Volatile.Read<Node>(ref buckets[i]);
while (current != null)
{
yield return new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
current = current.m_next;
}
}
}
В этой реализации есть техника под названием «ленивые вычисления» возвращение оператор вернет значение. Когда потребитель вызывает MoveNext(), вы вернетесь к «current = current.m_next;» Итак, в GetEnumerator() нет проверки «без изменений».
, если вы хотите, чтобы избежать исключения с «Словарь редактирования», то: 1. итерация к элементу, который требуется удалить 2. удалить элемент 3. перерыв перед MoveNext() называется
В вашей пример:
foreach (var d in dict2)
{
if (dict2.ContainsKey(1))
dict2.Remove(1);
if (dict2.ContainsKey(3))
dict2.Remove(3);
break; // will prevent from exception
}
для получения дополнительной информации о GetEnumerator() из ConcurrentDictionary: https://msdn.microsoft.com/en-us/library/dd287131(v=vs.110).aspx
Потому что они разные классы, они ведут себя иначе. 'ConcurrentDictionary' предназначен для использования с несколькими потоками, где можно удалить элемент, а другой - итерации, и, таким образом, он не генерирует исключение. –
Я знаю, что это разные классы, но я думал, что концепция только для чтения относится ко всем классам сбора. – BKS
Нет, не все классы вызывают исключение, если модифицировать его как перечисление. Исключениями являются классы в пространстве имен 'System.Collections.Concurrent'. Также 'ControlCollection' в winforms - это тот, который я помню, который не выбрасывает это исключение. –