Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify array or collection with foreach

Tags:

c#

I noticed something interesting. I can change the elements of the array or assign a new array reference in foreach blocks, but I cannot do the same operations with collections, it gives an error. Why?? The following example gives an error

List<string> isimler = new List<string>() { "Ali", "Ayşe", "Fatma", "Mehmet" };

foreach (var (isim, index) in isimler.Select((isim, index) => (isim, index)))
{
    isimler[index] += 10;
}

foreach(string isim in isimler)
{
    Console.WriteLine(isim);
}

The following example does not give an error, It's working

string[] isimler = { "Ali", "Ayşe", "Fatma", "Mehmet" };

foreach (var (isim, index) in isimler.Select((isim, index) => (isim, index)))
{
    isimler[index] += 10;
}

foreach(string isim in isimler)
{
    Console.WriteLine(isim);
}
like image 544
Ekin Ekin Avatar asked Dec 22 '25 06:12

Ekin Ekin


1 Answers

Because lists have code for explicit tracking for this (changing a tracking integer every time you make a change through the list API), and arrays do not. In the case of lists, this is especially important because if you add or remove elements it could significantly adversely impact the behaviour of the loop (imagine removing the zeroth element, for example). Since you cannot add/remove with arrays, this is less of a concern, and arrays are intended as very raw data types, without the overhead of such additional tracking fields.

As it happens there are APIs you could use with List<T> to have this specific change not be noticed. That doesn't make it a good idea to do so!

// DO NOT EVER DO THIS! Bad code! Only for illustration!
// This has *significant* risk of data corruption if you don't know
// **exactly** what you're doing.
var span = CollectionsMarshal.AsSpan(isimler);
foreach (var (isim, index) in isimler.Select((isim, index) => (isim, index)))
{
    span[index] += 10;
}

and if you were going to do that: this would be much more efficient:

// STILL NOT A GOOD IDEA!
// or in isimler.AsSpan() in the case of string[]
foreach (ref string isim in CollectionsMarshal.AsSpan(isimler))
{
    isim += 10;
}

Ultimately, though, in either event: if the contents of something are changing while you're looking at it: something risky is happening, whether or not it gets noticed.

like image 104
Marc Gravell Avatar answered Dec 23 '25 21:12

Marc Gravell