Пройдя через сумасшедший, когда он может быть изменен?
Я хочу сделать цикл foreach, вынимая членов этого цикла foreach, но это бросает ошибки. Моя единственная идея - создать еще один список внутри этого цикла, чтобы найти, какие фрагменты удалить, и пропустить новый список, чтобы удалить элементы из Pizza.
foreach(var Slice in Pizza)
{
if(Slice.Flavor == "Sausage")
{
Me.Eat(Slice); //This removes an item from the list: "Pizza"
}
}
Ответы
Ответ 1
Вы можете сделать это, на самом деле самый простой способ, который я нашел (например, думать, что я его придумал, уверен, что не правда, хотя;))
foreach (var Slice in Pizza.ToArray())
{
if (Slice.Flavor == "Sausage") // each to their own.. would have gone for BBQ
{
Me.Eat(Slice);
}
}
.. потому что он итерирует по фиксированной копии цикла. Он будет перебирать все элементы, даже если они удалены.
Удобно, не так ли?
(Кстати, ребята, это удобный способ итерации через копию коллекции, с безопасностью потока и удаление времени блокировки объекта: Lock, получить копию ToArray(), отпустить блокировку, затем итерацию)
Надеюсь, что это поможет!
Ответ 2
Если вам нужно выполнить итерацию по списку и удалить элементы, повторите ее с помощью цикла for:
// taken from Preet Sangha answer and modified
for(int i = Pizza.Count-1; i >= 0, i--)
{
var Slice = Pizza[i];
if(Slice.Flavor == "Sausage")
{
Me.Eat(Slice); //This removes an item from the list: "Pizza"
}
}
Причина повторения итерации заключается в том, что когда вы удаляете Elements, вы не запускаете исключение IndexOutOfRangeException, вызванное доступом к Pizza [5] на пицце, которая имеет только 5 элементов, потому что мы удалили шестой.
Причина использования цикла for заключается в том, что переменная i-итератора я не имеет отношения к Pizza, поэтому вы можете модифицировать пиццу без перерассеяния "break"
Ответ 3
используйте цикл for, а не foreach
for(int i = 0; i < in Pizza.Count(), ++i)
{
var Slice = Pizza[i];
if(Slice.Flavor == "Sausage")
{
Me.Eat(Slice); //This removes an item from the list: "Pizza"
}
}
Ответ 4
Возможно, самым ясным способом приблизиться к этому было бы создание списка ломтиков, чтобы поесть, а затем обработать его, избегая изменения исходного перечисления в цикле. Я никогда не был поклонником использования индексированных циклов для этого, поскольку он может быть подвержен ошибкам.
List<Slice> slicesToEat=new List<Slice>();
foreach(var Slice in Pizza)
{
if(Slice.Flavor == "Sausage")
{
slicesToEat.Add(Slice);
}
}
foreach(var slice in slicesToEat)
{
Me.Eat(slice);
}
Ответ 5
Возможно, замените свою подпись Me.Eat()
на IEnumerable<Slice>
Me.Eat(Pizza.Where(s=>s.Flavor=="Sausage").ToList());
Это позволяет выполнить задачу в 1 строке кода.
Тогда ваш Eat()
может выглядеть следующим образом:
public void Eat(IEnumerable<Slice> remove)
{
foreach (Slice r in remove)
{
Pizza.Remove(r);
}
}
Ответ 6
Объект "Коллекция", созданный в стиле VB6, допускает модификацию во время перечисления и, похоже, работает разумно, когда происходят такие изменения. Слишком плохо, что у него есть другие ограничения (тип ключа ограничен строками без учета регистра) и не поддерживает generics, поскольку ни один из других типов коллекций не позволяет изменять.
Откровенно говоря, я не понимаю, почему контракт Microsoft iEnumerable требует, чтобы исключение было выбрано, если коллекция была изменена. Я бы понял, что исключение будет выбрано, если изменения в коллекции сделают невозможным продолжение перечисления без ошибок (пропуская или дублируя значения, которые не изменялись при перечислении, сбоях и т.д.), Но не видят причин, по которым разрешить сбор, который мог бы разумно перечислить это.
Ответ 7
Где вы можете заказать пиццу, где на срезах есть отдельные начинки? В любом случае...
Использование Linq:
// Was "Me.Eat()" supposed to be "this.Eat()"?
Pizza
.Where(slice => slice.Flavor == "Sausage")
.Foreach(sausageSlice => { Me.Eat(sausageSlice); });
Первые две строки создают новый список только с обрезками колбасы. Третий возьмет это новое подмножество и передаст каждый фрагмент Me.Eat(). {И;} могут быть излишними. Это не самый эффективный метод, потому что он сначала делает копию (как и многие другие подходы, которые были даны), но она, безусловно, чистая и читаемая.
Кстати, это только для потомков, поскольку лучший ответ уже был задан - итерация назад по индексу.
Ответ 8
Какая коллекция - пицца? Если это List <T> то вы можете вызвать метод RemoveAll
:
Pizza.RemoveAll(slice => string.Equals(slice.Flavor, "Sausage"));