19

I have a construction similar to this (but a lot more complicated):

var list = new List<string>();

// .. populate list ..

foreach(var item in list)
{
    DoFunction(list);
}

public void DoFunction(List<string> list)
{
    if(someCondition == true)
    {
        // .. modify list in here ..
    }
}

Now, I understand that its not possible to edit the collection you're foreaching through, but how do you jump out of the loop gracefully if you do have to edit the list (without a try catch statement)? Is there some way to tell if the list has been edited? Can you edit the list and quickly break; before it notices?

Entity
  • 7,972
  • 21
  • 79
  • 122
  • IIRC it will throw a concurrent modification exception. – Woot4Moo Jun 09 '11 at 15:06
  • Whats the problem with exiting from the loop if some condition occurs and editting the list after that? – FIre Panda Jun 09 '11 at 15:06
  • possible duplicate of [How to modify or delete items from an enumerable collection while iterating through it in C#](http://stackoverflow.com/questions/308466/how-to-modify-or-delete-items-from-an-enumerable-collection-while-iterating-throu) – Woot4Moo Jun 09 '11 at 15:06
  • You could `List.Where(somecondition).ToList().Foreach(List.Remove(thisitem))`, only for small lists though... not efficient for larger ones. – FlyingStreudel Jun 09 '11 at 15:07
  • @Abdul Because the loop has no idea about the condition. It's all done in `DoFunction()` – Entity Jun 09 '11 at 15:07
  • 1
    I wouldn't try modifying and then breaking. It might work for some enumerators, but break for others (those whose underlying processing is asynchronous, for example). – Jim Mischel Jun 09 '11 at 15:07
  • 1
    you'd have to break from the loop after changing the list, so that the MoveNext method of the IEnumerator interface is not called to position the iterator to the next element – Marius Bancila Jun 09 '11 at 15:08
  • Possible duplicate of [What is the best way to modify a list in a 'foreach' loop?](https://stackoverflow.com/questions/759966/what-is-the-best-way-to-modify-a-list-in-a-foreach-loop) – StayOnTarget Jul 18 '18 at 11:55

3 Answers3

35

Yes, you could break, if that's what you really want. An exception won't be thrown until the for loop tries to grab the next item from the list.

But I've found it's easiest just to create and iterate across a copy of the list so you don't have to worry about it.

foreach(var item in list.ToList())

The added performance overhead of an extra, untouched list is generally negligible compared to the maintainability costs of more complex code.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • Doesn't that present the same problem? if `list` changes, then wouldn't `list.ToList()` change too? – Entity Jun 09 '11 at 15:09
  • 2
    @TheAdamGaskins: No, `list.ToList()` gets evaluated once at the beginning of the `for` loop, and just creates a direct copy of the original list. That copy is used for the sake of iteration, whereas the original list is the one with items being added and removed. – StriplingWarrior Jun 09 '11 at 15:12
  • 1
    @TheAdamGaskins: No, because `list.ToList()` will be evaluated and a new list created. The enumerator will enumerate the new list, leaving the original list available for modification. – Jim Mischel Jun 09 '11 at 15:12
  • Indeed: ToList() evaluates eagerly a.k.a. immediately, rather than lazily. A nice table of Linq methods and their evaluation styles: http://visualcsharptutorials.com/linq/deferred-execution (scroll down to the bottom) – Timo Mar 30 '15 at 09:07
29

Rather than use a foreach construct, a for loop would allow you to alter the list.

for (var x = 0; x < list.Count; x++) {

}
Fosco
  • 38,138
  • 7
  • 87
  • 101
  • 4
    This can get really tricky, depending on which modifications are made to the original list. If you remove items that you find while traversing the list, you have to modify the `x` value appropriately to avoid errors. – StriplingWarrior Jun 09 '11 at 15:14
  • 2
    @StriplingWarrior it isn't that hard. Just do `x--` and you're fine after deleting – Oskar Kjellin Jun 09 '11 at 15:15
  • 2
    I depends on how complicated his DoFunction is. If he's removing *other* items from the list (besides the item at `x`), then he'll have to determine whether those items come before or after the current position. I'm not saying it can't be done--it just becomes complex enough that it's not worth it in many cases. – StriplingWarrior Jun 09 '11 at 15:26
  • DoFunction is *very* complicated actually... but at the most it will only remove one element, so this works perfectly :) – Entity Jun 09 '11 at 16:12
  • @Timo: Actually it's not random access, according to the documentation accessing by index is an O(1) operation, so it's perfectly fine to directly access the list regardless of the number of items: msdn.microsoft.com/en-us/library/0ebtbkkc.aspx – Trisibo Nov 21 '15 at 21:20
  • @DED You're absolutely right. I'm deleting my faulty comment. Random access in a List is indeed O(1). – Timo Nov 23 '15 at 09:40
3

It's hard to offer useful advice without knowing what kinds of edits are being made. The pattern that I've found is has the most general-purpose value, though, to just construct a new list.

For example, if you need to look at each item and decide between removing it, leaving it as-is, or inserting items after it, you could use a pattern like this:

IEnumerable<string> butcherTheList(IEnumerable<string> input)
{
    foreach (string current in input)
    {
        if(case1(current))  
        {
            yield return current;
        }
        else if(case2(current))
        {
            yield return current;
            yield return someFunc(current);
        }
        // default behavior is to yield nothing, effectively removing the item
    }
}

List<string> newList = butcherTheList(input).ToList();
Sean U
  • 6,730
  • 1
  • 24
  • 43