Есть ли какие-либо наборы C#, где модификация не делает недействительным итераторы?

Есть ли какие-либо структуры данных в библиотеке C# Collections, где модификация структуры не делает недействительным итераторы?

Рассмотрите следующее:

List<int> myList = new List<int>();
myList.Add( 1 );
myList.Add( 2 );
List<int>.Enumerator myIter = myList.GetEnumerator();
myIter.MoveNext();  // myIter.Current == 1
myList.Add( 3 );
myIter.MoveNext();  // throws InvalidOperationException
10
задан Will Vousden 2 May 2010 в 14:22
поделиться

6 ответов

Да, посмотрите на пространство имен System.Collections.Concurrent в .NET 4.0.

Обратите внимание, что для некоторых коллекций в этом пространстве имен (например, ConcurrentQueue) это работает только за счет раскрытия перечислителя на "снимке" рассматриваемой коллекции.

Из документации MSDN по ConcurrentQueue:

Перечисление представляет собой моментальный снимок содержимого очереди. Оно не отражает любые обновления коллекции после вызова GetEnumerator. перечислитель безопасен для одновременного использования с чтением из и записью в очередь.

Однако это не относится ко всем коллекциям. ConcurrentDictionary, например, дает вам перечислитель, который поддерживает обновления базовой коллекции между вызовами MoveNext.

Из документации MSDN по ConcurrentDictionary:

Перечислитель, возвращаемый из словаря, безопасен для одновременного использования с чтением и записью в словаря, однако он не представляет собой моментальный снимок словаря. словаря. Содержимое, отображаемое через перечислитель может содержать изменения, внесенные в словарь после вызова GetEnumerator.

Если у вас нет 4.0, то я думаю, что другие правы и такой коллекции в .NET не предусмотрено. Однако вы всегда можете создать свою собственную, сделав то же самое, что делает ConcurrentQueue (итерация по снимку).

11
ответ дан 3 December 2019 в 16:51
поделиться

Согласно этой статье MSDN о IEnumerator поведение аннулирования, которое вы обнаружили, требуется во всех реализациях IEnumerable.

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

8
ответ дан 3 December 2019 в 16:51
поделиться

Единственный способ сделать это - создать копию списка перед его итерацией:

var myIter = new List<int>(myList).GetEnumerator();
1
ответ дан 3 December 2019 в 16:51
поделиться

Поддержка такого поведения требует довольно сложной внутренней обработки, поэтому большинство коллекций не поддерживают этого (я не уверен насчет пространства имен Concurrent).

Однако вы можете очень хорошо имитировать такое поведение, используя неизменяемые коллекции. Они не позволяют вам изменять коллекцию по дизайну, но вы можете работать с ними несколько иначе, и такая обработка позволяет вам использовать перечислитель параллельно без сложной обработки (реализовано в Concurrent коллекциях).

Вы можете легко реализовать подобную коллекцию, или использовать FSharpList из FSharp.Core.dll (хотя это не стандартная часть .NET 4.0):

open Microsoft.FSharp.Collections;

// Create immutable list from other collection
var list = ListModule.OfSeq(anyCollection);
// now we can use `GetEnumerable`
var en = list.GetEnumerable();

// To modify the collection, you create a new collection that adds 
// element to the front (without actually copying everything)
var added = new FSharpList<int>(42, list);

Преимущество неизменяемых коллекций в том, что вы можете работать с ними (создавая копии), не затрагивая оригинал, и таким образом, поведение, которое вы хотели, получается "бесплатно". Для получения дополнительной информации есть отличная серия статей Эрика Липперта.

5
ответ дан 3 December 2019 в 16:51
поделиться

Нет, они не существуют. Стандартные коллекции ALl C# аннулируют числитель при изменении структуры.

0
ответ дан 3 December 2019 в 16:51
поделиться

Используйте цикл for вместо foreach, и тогда вы сможете изменить его. Хотя я бы не советовал этого делать....

-1
ответ дан 3 December 2019 в 16:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: