Безопасно ли добавлять элементы в список при его повторении?
Я понимаю, что нельзя использовать счетчик и вносить изменения в List<T>
в то же время. Добавление или удаление элемента приведет к аннулированию перечислителя ( http://msdn.microsoft.com/en-us/library/system.collections.ienumerator(v=vs.110).aspx), поскольку внутренний массив будет перераспределен ( http://msdn.microsoft.com/en-us/library/3wcytfd1(v=vs.110).aspx).
- Всегда ли это правда, даже если
Count
меньше чемCapacity
?(Насколько я понимаю, в этом случае массив не будет перераспределен, поэтому перечислитель должен быть действительным); - Зачем
Current
возвращает элемент, для которого он установлен, даже если перечислитель уже признан недействительным?(я имею в виду, если массив был перераспределен...); - Сохраняет ли перераспределение первоначальный порядок элементов? Я имею в виду, если какой-то элемент имеет индекс n, будет ли он иметь тот же индекс после добавления и элемента?(MSDN говорит, что
Add
добавляет объект в конец списка) Если это так, то можно безопасно просматривать список в обычном режиме.for
цикл и добавление элементов в предположении, что цикл будет проходить через каждый элемент только один раз, я прав?
3 ответа
Всегда ли это правда, даже если
Count
меньше чемCapacity
Это может или не может быть правдой. Однако вы должны действовать так, как будто это всегда верно, потому что документация не делает никаких исключений для этого поведения.
По крайней мере, одна реализация List<T>
(из проекта Mono) всегда делает недействительными все итераторы, увеличивая скрытый член с именем _version
который хранится внутри списка, в каждой операции, которая изменяет список. Когда вы получаете итератор (который называется Enumerator
в.NET) текущее значение _version
хранится внутри объекта перечислителя. Каждый раз, когда вы звоните MoveNext
хранится _version
по сравнению с текущим _version
, и исключение выдается, когда два не совпадают.
Зачем
Current
возвращает элемент, для которого он установлен, даже если перечислитель уже признан недействительным?
Потому что ценность Current
хранится в итераторе. Несмотря на то, что есть веская причина, чтобы помешать вам повторять итерацию, когда коллекция была изменена, хорошо позволить вам получить доступ к значению в той позиции, до которой вы продвинули итератор, когда он был действителен.
Сохраняет ли перераспределение первоначальный порядок элементов?
Да. Вот почему обход списка с for
Цикл и индекс остается безопасным, даже когда вы добавляете или удаляете элементы из списка.
Внутренний массив не перераспределяется каждый раз, когда элементы добавляются / удаляются из списка, и это не причина, по которой перечислитель становится недействительным. Вот лучшая причина: коллекция была изменена; Операция перечисления может не выполняться - почему?,
Проще говоря, внутренний массив перераспределяется, когда его емкость исчерпана, и размер массива будет удвоен. Некоторые коллекции также позволяют сократить внутренний массив, например,
List<T>.TrimExcess
Почему бы и нет? Объект все еще существует.
да
Посмотрите на исходный код, особенно на EnsureCapacity
а также List<T>.Enumerator
если вы хотите узнать больше.
Добавление или удаление элемента приведет к аннулированию перечислителя, поскольку внутренний массив будет перераспределен
Это частично правильно. Добавление, удаление или вставка элементов приведет к недействительности перечислителя не потому, что внутренний массив будет перераспределен, а потому, что List<T>
поддерживает version
поле, чтобы отслеживать изменения, которые произошли. Enumerator будет использовать поле версии, чтобы найти какие-либо новые обновления, произошедшие с момента создания enumerator.
Чтобы ответить на ваши вопросы:
- Перечислитель недействителен[ 1], если вы изменяете список независимо от "Count
- Потому что перечислитель берет копию текущего элемента из списка.
- Да перераспределение сохраняет порядок.
1: Это не всегда так, в реализации есть ошибка.