Каков наилучший способ изменить список в цикле «foreach»?
101774 просмотра
11 ответа
Новая функция в C # / .NET 4.0 заключается в том, что вы можете изменить свой перечисляемый объект в foreach
без получения исключения. См. Запись в блоге Пола Джексона « Интересный побочный эффект параллелизма: удаление элементов из коллекции при перечислении» для получения информации об этом изменении.
Каков наилучший способ сделать следующее?
foreach(var item in Enumerable)
{
foreach(var item2 in item.Enumerable)
{
item.Add(new item2)
}
}
Обычно я использую IList
в качестве кеша / буфера до конца foreach
, но есть ли лучший способ?
Ответы (11)
69 плюса
Коллекция, используемая в foreach, является неизменной. Это очень много задумано.
Как говорится в MSDN :
Оператор foreach используется для итерации всей коллекции, чтобы получить необходимую информацию, но его нельзя использовать для добавления или удаления элементов из исходной коллекции, чтобы избежать непредсказуемых побочных эффектов. Если вам нужно добавить или удалить элементы из исходной коллекции, используйте цикл for.
Сообщение в ссылке, предоставленной Poko, указывает, что это разрешено в новых одновременных коллекциях.
Автор: Rik Размещён: 17.04.2009 10:5615 плюса
Сделайте копию перечисления, используя в этом случае метод расширения IEnumerable, и перечислите его. Это добавит копию каждого элемента в каждом внутреннем перечисляемом для этого перечисления.
foreach(var item in Enumerable)
{
foreach(var item2 in item.Enumerable.ToList())
{
item.Add(item2)
}
}
Автор: tvanfosson
Размещён: 17.04.2009 10:56
8 плюса
Как уже упоминалось, но с примером кода:
foreach(var item in collection.ToArray())
collection.Add(new Item...);
Автор: eulerfx
Размещён: 28.04.2009 11:36
7 плюса
Чтобы проиллюстрировать ответ Nippysaurus: Если вы собираетесь добавить новые элементы в список и хотите обрабатывать вновь добавленные элементы также во время того же перечисления, то вы можете просто использовать цикл for вместо цикла foreach , проблема решена :)
var list = new List<YourData>();
... populate the list ...
//foreach (var entryToProcess in list)
for (int i = 0; i < list.Count; i++)
{
var entryToProcess = list[i];
var resultOfProcessing = DoStuffToEntry(entryToProcess);
if (... condition ...)
list.Add(new YourData(...));
}
Для работающего примера:
void Main()
{
var list = new List<int>();
for (int i = 0; i < 10; i++)
list.Add(i);
//foreach (var entry in list)
for (int i = 0; i < list.Count; i++)
{
var entry = list[i];
if (entry % 2 == 0)
list.Add(entry + 1);
Console.Write(entry + ", ");
}
Console.Write(list);
}
Вывод последнего примера:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3, 5, 7, 9,
Список (15 наименований)
0
1
2
3
4
5
6
7
8
9
1
3
5
7
9
4 плюса
Вот как вы можете это сделать (быстрое и грязное решение. Если вам действительно нужно такое поведение, вам следует либо пересмотреть свой дизайн, либо переопределить все IList<T>
элементы и объединить список источников):
using System;
using System.Collections.Generic;
namespace ConsoleApplication3
{
public class ModifiableList<T> : List<T>
{
private readonly IList<T> pendingAdditions = new List<T>();
private int activeEnumerators = 0;
public ModifiableList(IEnumerable<T> collection) : base(collection)
{
}
public ModifiableList()
{
}
public new void Add(T t)
{
if(activeEnumerators == 0)
base.Add(t);
else
pendingAdditions.Add(t);
}
public new IEnumerator<T> GetEnumerator()
{
++activeEnumerators;
foreach(T t in ((IList<T>)this))
yield return t;
--activeEnumerators;
AddRange(pendingAdditions);
pendingAdditions.Clear();
}
}
class Program
{
static void Main(string[] args)
{
ModifiableList<int> ints = new ModifiableList<int>(new int[] { 2, 4, 6, 8 });
foreach(int i in ints)
ints.Add(i * 2);
foreach(int i in ints)
Console.WriteLine(i * 2);
}
}
}
Автор: Anton Gogolev
Размещён: 17.04.2009 11:02
2 плюса
LINQ очень эффективен для жонглирования коллекциями.
Ваши типы и структура мне неясны, но я постараюсь соответствовать вашему примеру в меру своих возможностей.
Из вашего кода видно, что для каждого элемента вы добавляете к этому элементу все из его собственного свойства Enumerable. Это очень просто:
foreach (var item in Enumerable)
{
item = item.AddRange(item.Enumerable));
}
В качестве более общего примера, скажем, мы хотим перебрать коллекцию и удалить элементы, для которых выполняется определенное условие. Избегая foreach
, используя LINQ:
myCollection = myCollection.Where(item => item.ShouldBeKept);
Добавить элемент на основе каждого существующего элемента? Нет проблем:
myCollection = myCollection.Concat(myCollection.Select(item => new Item(item.SomeProp)));
Автор: Timo
Размещён: 30.03.2015 08:53
1 плюс
Вы не можете изменить перечисляемую коллекцию во время ее перечисления, поэтому вам придется вносить изменения до или после перечисления.
for
Петля является хорошей альтернативой, но если ваша IEnumerable
коллекция не реализует ICollection
, не представляется возможным.
Или:
1) Сначала скопируйте коллекцию. Перечислите скопированную коллекцию и измените исходную коллекцию во время перечисления. (@Tvanfosson)
или же
2) Вести список изменений и фиксировать их после перечисления.
Автор: Josh G Размещён: 17.04.2009 12:171 плюс
Наилучшим подходом с точки зрения производительности, вероятно, является использование одного или двух массивов. Скопируйте список в массив, выполните операции с массивом, а затем создайте новый список из массива. Доступ к элементу массива быстрее, чем доступ к элементу списка, и преобразования между a List<T>
и a T[]
могут использовать быструю операцию «массового копирования», которая позволяет избежать накладных расходов, связанных с доступом к отдельным элементам.
Например, предположим, что у вас есть List<string>
и вы хотите, чтобы каждая строка в списке начиналась с T
символа «Boo», а каждая строка, начинающаяся с «U», полностью отбрасывалась. Оптимальный подход, вероятно, будет что-то вроде:
int srcPtr,destPtr;
string[] arr;
srcPtr = theList.Count;
arr = new string[srcPtr*2];
theList.CopyTo(arr, theList.Count); // Copy into second half of the array
destPtr = 0;
for (; srcPtr < arr.Length; srcPtr++)
{
string st = arr[srcPtr];
char ch = (st ?? "!")[0]; // Get first character of string, or "!" if empty
if (ch != 'U')
arr[destPtr++] = st;
if (ch == 'T')
arr[destPtr++] = "Boo";
}
if (destPtr > arr.Length/2) // More than half of dest. array is used
{
theList = new List<String>(arr); // Adds extra elements
if (destPtr != arr.Length)
theList.RemoveRange(destPtr, arr.Length-destPtr); // Chop to proper length
}
else
{
Array.Resize(ref arr, destPtr);
theList = new List<String>(arr); // Adds extra elements
}
Было бы полезно, если бы List<T>
предоставили метод для построения списка из части массива, но я не знаю ни одного эффективного метода для этого. Тем не менее, операции над массивами довольно быстрые. Следует отметить тот факт, что добавление и удаление элементов из списка не требует «проталкивания» вокруг других элементов; каждый элемент записывается непосредственно в соответствующее место в массиве.
0 плюса
Вы должны действительно использовать for()
вместо foreach()
этого.
0 плюса
Чтобы добавить к ответу Тимо LINQ, можно использовать также вот так:
items = items.Select(i => {
...
//perform some logic adding / updating.
return i / return new Item();
...
//To remove an item simply have logic to return null.
//Then attach the Where to filter out nulls
return null;
...
}).Where(i => i != null);
Автор: DDiVita
Размещён: 21.09.2018 12:50
0 плюса
Я написал один легкий шаг, но из-за этого производительность будет ухудшаться
Вот мой фрагмент кода: -
for (int tempReg = 0; tempReg < reg.Matches(lines).Count; tempReg++)
{
foreach (Match match in reg.Matches(lines))
{
var aStringBuilder = new StringBuilder(lines);
aStringBuilder.Insert(startIndex, match.ToString().Replace(",", " ");
lines[k] = aStringBuilder.ToString();
tempReg = 0;
break;
}
}
Автор: pravin ghare
Размещён: 30.10.2018 09:13
Вопросы из категории :
- c# Преобразовать десятичную в двойную?
- c# Как рассчитать чей-то возраст в C #?
- c# Как вы сортируете словарь по значению?
- c# В чем разница между int и Integer в Java и C #?
- .net Как создать новый экземпляр объекта из Типа
- .net Действительно ли опечатанные классы действительно предлагают преимущества?
- list Функция транспонирования / распаковки (обратная сторона zip)?
- list How would you make a comma-separated string from a list of strings?
- list Удалить дубликаты из списка <T> в C #
- list Console.WriteLine и общий список
- enumeration Цикл C # - разрыв против продолжения
- enumeration Как мне перечислить перечисление в C #?
- enumeration PHP и перечисления
- enumeration Идентификатор foreach и замыкания
- enumerable Executing a certain action for all elements in an Enumerable<T>
- enumerable Каков наилучший способ изменить список в цикле «foreach»?
- enumerable IEnumerable не имеет метода Count
- enumerable Группируйте хэши по ключам и суммируйте значения