Why can't I modify the loop variable in a foreach?

后端 未结 6 1576
孤城傲影
孤城傲影 2020-12-03 15:21

Why is a foreach loop a read only loop? What reasons are there for this?

相关标签:
6条回答
  • 2020-12-03 15:56

    The foreach command uses the IEnumerable interface to loop throught the collection. The interface only defined methods for stepping through a collection and get the current item, there is no methods for updating the collection.

    As the interface only defines the minimal methods required to read the collecton in one direction, the interface can be implemented by a wide range of collections.

    As you only access a single item at a time, the entire collection doesn't have to exist at the same time. This is for example used by LINQ expressions, where it creates the result on the fly as you read it, instead of first creating the entire result and then let you loop through it.

    0 讨论(0)
  • 2020-12-03 16:01

    foreach works with everything implementing the IEnumerable interface. In order to avoid synchronization issues, the enumerable shall never be modified while iterating on it.

    The problems arise if you add or remove items in another thread while iterating: depending on where you are you might miss an item or apply your code to an extra item. This is detected by the runtime (in some cases or all???) and throws an exception:

    System.InvalidOperationException was unhandled
      Message="Collection was modified; enumeration operation may not execute."
    

    foreach tries to get next item on each iteration which can cause trouble if you are modifying it from another thread at the same time.

    0 讨论(0)
  • 2020-12-03 16:05

    I would assume it's how the iterator travels through the list.

    Say you have a sorted list:

    Alaska
    Nebraska
    Ohio
    

    In the middle of

    foreach(var s in States)
    {
    }
    

    You do a States.Add("Missouri")

    How do you handle that? Do you then jump to Missouri even if you're already past that index.

    0 讨论(0)
  • 2020-12-03 16:05

    Not sure what you mean with read-only but I'm guessing that understanding what the foreach loop is under the hood will help. It's syntactic sugar and could also be written something like this:

    IEnumerator enumerator = list.GetEnumerator();
    while(enumerator.MoveNext())
    {
       T element = enumerator.Current;
       //body goes here
    }
    

    If you change the collection (list) it's getting hard to impossible to figure out how to process the iteration. Assigning to element (in the foreach version) could be viewed as either trying to assign to enumerator.Current which is read only or trying to change the value of the local holding a ref to enumerator.Current in which case you might as well introduce a local yourself because it no longer has anything to do with the enumerated list anymore.

    0 讨论(0)
  • 2020-12-03 16:11

    If, by this, you mean:

    Why shouldn't I modify the collection that's being foreach'd over?

    There's no surety that the items that you're getting come out in a given order, and that adding an item, or removing an item won't cause the order of items in the collection to change, or even the Enumerator to become invalid.

    Imagine if you ran the following code:

    var items = GetListOfTOfSomething(); // Returns 10 items
    
    int i = 0;
    foreach(vat item in items)
    {
        i++;
        if (i == 5)
        {
            items.Remove(item);
        }
    }
    

    As soon as you hit the loop where i is 6 (i.e. after the item is removed) anything could happen. The Enumerator might have been invalidated due to you removing an item, everything might have "shuffled up by one" in the underlying collection causing an item to take the place of the removed one, meaning you "skip" one.

    If you meant "why can't I change the value that is provided on each iteration" then, if the collection you're working with contains value types, any changes you make won't be preserved as it's a value you're working with, rather than a reference.

    0 讨论(0)
  • 2020-12-03 16:13

    I'm not sure exactly what you mean by a "readonly loop" but I'm guessing that you want to know why this doesn't compile:

    int[] ints = { 1, 2, 3 };
    foreach (int x in ints)
    {
        x = 4;
    }
    

    The above code will give the following compile error:

    Cannot assign to 'x' because it is a 'foreach iteration variable'
    

    Why is this disallowed? Trying to assigning to it probably wouldn't do what you want - it wouldn't modify the contents of the original collection. This is because the variable x is not a reference to the elements in the list - it is a copy. To avoid people writing buggy code, the compiler disallows this.

    0 讨论(0)
提交回复
热议问题