-2

It is known that while iterating some IEnumerable in C#, no modifications can be made to elements of the enumerable collection:

// Illegal code
foreach (Employee e in employeeList)
{
     e.Salary = 1000000;
}

I am wondering how is this being enforced by the runtime or by the enumerator itself?

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
lysergic-acid
  • 19,570
  • 21
  • 109
  • 218
  • 1
    Why do you think that the code is illegal? Did you try compiling and running it? – Douglas Jun 09 '12 at 18:23
  • Code seems to be fine. But you cannot modify e itself. Like e = new Employee(); – Dmitry Harnitski Jun 09 '12 at 18:24
  • 3
    -1: The edit changes the *nature* of the question. You should invest more effort into deciding what you want to ask, before requesting us to invest time answering it. – Douglas Jun 09 '12 at 18:30
  • 1
    I gave a wrong example, however the nature of the question was well understood, and also well answered by all. – lysergic-acid Jun 09 '12 at 18:32
  • 1
    the fact that I was able to guess what you meant doesn't change the fact that what you asked ("modifying an IEnumerable element during a foreach loop") is legal, and is also *not* what your code sample is doing... – Michael Edenfield Jun 09 '12 at 18:33
  • 2
    Rolling back. The edits made the question/answers complete nonsense. – Kirk Woll Jun 09 '12 at 20:23

3 Answers3

7

I think you're misunderstanding the restriction. The code you posted is perfectly legal, and in fact, if it were not then the foreach loop would be pretty useless.

What is not permitted is changing the sequence while in the middle of a foreach loop:

foreach ( Employee e in employeeList )
{
  if ( e.Salary > 10000000 ) 
  {
    employeeList.Remove(e);
  }
}

This code throw an InvalidOperationException at run-time: the call to MoveNext in the IEnumerator implementation will throw when it detects that the underlying collection has changed between calls. This is a requirement of implementing the enumerator object -- each implementation of IEnumerable has to deal with this possibility for itself. Note that certain collections (the new concurrent ones from .NET 4.0) explicitly do not enforce this restrictions, and thus the code above would be legal of employeeList was, for example, a ConcurrentDictionary. See this blog post for details.

It is also not legal to assign a value to the loop control variable, e.g.:

foreach ( Employee e in employeeList )
{
  if ( e.Salary > 10000000 ) 
  {
    e = new Employee { Salary = 999999 };
  }
}

The reason this isn't allowed is because it doesn't make much sense and is almost certainly a bug; see this answer for details: Why is The Iteration Variable in a C# foreach statement read-only?

Note that this isn't attempting to change the elements in the sequence -- its merely attempting to change the value of the local variable used as the loop control variable. Also, this isn't enforced by the enumerator nor the run-time. It's a compile-time error, and is enforced by the C# compiler.

Community
  • 1
  • 1
Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117
2

What you are saying is not quite true. This code is perfectly legal.

foreach (Employee e in employeeList){
     e.Salary = 1000000;
}

However, this isn't.

foreach (Employee e in employeeList){
     e = new Employee();
}
GETah
  • 20,922
  • 7
  • 61
  • 103
2

The restriction (which isn't violated by your code, as noted in other answers) is enforced by the enumerator - if it chooses to do so. I believe some of the built-in collections such as List<T> keep an internal version number which is updated when the collection is changed, and checked on each iteration.

Some other classes such as ConcurrentBag don't check this, but make other guarantees around the behaviour of an enumerator when the collection is changed, whether that's in the same thread or not.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Using an internal version number makes me think one could write code which modifies a `List` without the runtime catching it. I think I'll peek at the reference source and investigate that... – Brian Jun 10 '12 at 17:30