Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying Collection when using a foreach loop in c# [duplicate]

Basically, I would like to remove an item from a list whilst inside the foreach loop. I know that this is possible when using a for loop, but for other purposes, I would like to know if this is achievable using a foreach loop.

In python we can achieve this by doing the following:

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

for i in a:
    print i

    if i == 1:
        a.pop(1)

This gives the following Output

>>>1
3
4
5
6
7
8
9

But when doing something similar in c#, I get an InvalidOperationException, I was wondering if there was a way of getting around this, without just simply using a for loop.

The code in c# that I used when the exception was thrown:

static void Main(string[] args)
  {
  List<string> MyList = new List<string>(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9"});

  foreach (string Item in MyList)
    {
    if (MyList.IndexOf(Item) == 0)
      {
      MyList.RemoveAt(1);
      }

    Console.WriteLine(Item);
    }
  }

Thanks in advance

like image 464
Lloyd Powell Avatar asked Jul 14 '09 08:07

Lloyd Powell


People also ask

Can we modify list in foreach?

Modify a C# collection with foreach by using a second collection. Since we cannot change a collection directly with foreach , an alternative is to use a second collection. To that second collection we then add new elements. This way we can still benefit from the simplicity that foreach offers.

How do I change the value of a foreach loop?

The value being iterated cannot be changed, because the foreach loop relies on the IEnumerator interface, and this interface has no method to change the value being enumerated.

Can we modify collection while iterating C#?

Collections support foreach statement using Enumarator. Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection.

Does foreach mutate array C#?

forEach() does not mutate the array on which it is called.


1 Answers

You can't do this. From the docs for IEnumerator<T>:

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.

Alternatives are:

  • Build up a new list of items to remove, then remove them all afterwards
  • Use a normal "for" loop and make sure you're careful about not going over the same element twice or missing any out. (You've said you don't want to do this, but what you're trying to do just won't work.)
  • Build a new collection containing only the elements you want to retain

The last of these alternatives is the LINQ-like solution, where you'd typically write:

var newList = oldList.Where(x => ShouldBeRetained(x)).ToList();

(Where ShouldBeRetained is whatever logic you want, of course.) The call to ToList() is only necessary if you actually want it in a list. This leads to more declarative code which is often easier to read. I can't easily guess what your original loop is meant to do (it seems pretty odd at the moment) whereas if you can express the logic purely in terms of the item, it can be a lot clearer.

like image 110
Jon Skeet Avatar answered Sep 19 '22 15:09

Jon Skeet