Reputation: 13129
I have the following code in C# utilizing foreach
. In one loop I am modifying a List<T>
, and in another, a string
array.
We can't directly assign a value or null to the iteration variable, but we can modify its properties, and the modifications are reflected in the List
finally.
So this basically means the iteration variable is a reference to the element in the list, so why we can't assign a value to it directly?
class Program
{
public static void Main(string[] args)
{
List<Student> lstStudents = Student.GetStudents();
foreach (Student st in lstStudents)
{
// st is modified and the modification shows in the lstStudents
st.RollNo = st.RollNo + 1;
// not allowed
st = null;
}
string[] names = new string[] { "me", "you", "us" };
foreach (string str in names)
{
// modifying str is not allowed
str = str + "abc";
}
}
}
The student class:
class Student
{
public int RollNo { get; set; }
public string Name { get; set; }
public static List<Student> GetStudents()
{
List<Student> lstStudents = new List<Student>();
lstStudents.Add(new Student() { RollNo = 1, Name = "Me" });
lstStudents.Add(new Student() { RollNo = 2, Name = "You" });
lstStudents.Add(new Student() { RollNo = 3, Name = "Us" });
return lstStudents;
}
}
Upvotes: 3
Views: 8813
Reputation: 109547
Here's a concrete example that should make it clear.
Imagine that you implement an IEnumerable<int>
like so:
public static IEnumerable<int> OneToTen()
{
for (int i = 1; i <= 10; ++i)
{
yield return i;
}
}
Given that, what would it mean if in the following loop you could assign to n
?
foreach (var n in OneToTen())
{
n = 1; // Change original "collection"? There isn't one!
}
The compiler could let you change n
without it having anything to do with changing the thing being iterated, but it would be misleading - which is why the compiler disallows it.
Upvotes: 1
Reputation: 39600
It's a reference type, of course you can modify its properties if they are writeable, and those changes will be reflected in the object the reference refers to.
A variable of a reference type is by definition a reference to an object. But that's something different than a reference to an actual storage location (such as a variable, or a list entry).
You can imagine it like a Student
variable contains just the address where to find the original object. By copying the list entry to a variable, you are just copying the address, so now you have two addresses, once in the list and once in your variable. By reassigning the variable, you are writing another address into it, but that doesn't affect the original entry in the list. Neither does it overwrite the object that the variable referred to earlier.
That being said, foreach
probably doesn't allow you to reassign the iteration variable because it could lead to confusion/bugs. The way it currently is implemented, you always know that the iteration variable is the current element.
Upvotes: 1
Reputation: 33139
This is because the compiler implements foreach
with an Enumerator
. Enumerators can only forward-move in a closed collection, they cannot alter the collection they iterate over.
Upvotes: 2
Reputation: 1062560
The iteration variable in a foreach
is not a "reference to the element in the list" - it is merely the value from .Current {get;}
in an iterator implementation obtained via GetEnumerator()
- most commonly via IEnumerator[<T>]
but not always - indeed for a List<T>
it is a List<T>.Enumerator
value. In the general case, there is no "meaning" to assigning to the iterator variable. Consider:
IEnumerable<int> RandomSequence(int count) {
var rand = new Random();
while(count-->0) yield return rand.Next();
}
This will work identically from a foreach
- what would it mean to assign to it?
Thus, foreach
offers no facility to assign to the iterator variable.
Upvotes: 9