C# question - switching array while looping over it in foreach

Is this allowed or is this unreliable/unpredictable?:

int[] testArr = new int[] {1,2,3,4,5};

for (int i = 1; i < 3; i++)
{
    foreach (int item in testArr)
    {
        if (item == 3)
        {
            testArr = new int[] { -1, -2};
        }
        Debug.Log(item);
    }
}

The output:

1
2
3
4
5
-1
-2

I read on several occasions that it is not allowed to do something like that but it works and I don’t get an error message in the compiler like on other occasions, when you change the element you are iterating over. But it looks like it is preventing that on its own.

Thanks

Alex

A foreach loop is really just syntax sugar for basically this:

var e = testArr.GetEnumerator();
while (e.MoveNext())
{
    var item = e.Current;

    //your code in the loop
}

Switching out testArr wouldn’t work because GetEnumerator was already called before it got to the point where you set it.

This is why in your outputs even though you replaced it on 3, you get 1-5. But you then get the -1,-2, because your for loop has moved onto i == 2 and looped the new array you assigned to testArr.

So does it result in something that does a thing? Yes.

But that thing is unintuitive and not obvious when reading the code.

Hehe… if only that was the only occasion that happens in my coding :wink:

In my case it is actually very intuitive, since i am having an array with the directions up, down, left, right as vectors for a bomb explosion direction. Depending on the gameplay certain directions should be blocked on the next outer iteration, so I remove them.

My main question really was, if it is reliable to use or just works coincidentally.

Thanks for clearing this up for me.

Arrays are never a problem as you can not change an array in size. Arrays have a fix length that can not change. You replaced the array referenced in the variable with a new array. However as lordofduct already explained, foreach internally uses the IEnumerable pattern.

However generic List classes allow the manipulation of the list (adding / removing elements). The List class comes with its own enumerator that stores a “version” integer internally. Whenever the version of the source list is changed, the enumerator explicitly throws an exception as the iteration logic may result in unpredictable behaviour. Replacing a List with a new list would also work just fine since you’re iterating over a completely different list instance.

Though while you could “replace” elements of an array inside a foreach loop, you can not do that with a List as the internal enumerator also increases the version when you assign new values.

Just because it results in an outcome you want does not make it intuitive or readable. That just means it does what you want.

I used a drive a car whose doors had no handles. The driver side door the handle broke off far inside the door, the passenger door a small metal rod sort of stuck out of the hold where the handle used to be bolted. I would take my shirt and bundle up a bit of it to protect my thumb while I pushed down on the rod opening the passenger door. Then I’d climb across to my driver door.

This option worked.

But it wasn’t intuitive nor easy to understand.

I’m not telling you that you can’t do what you’re doing. Swapping out the reference of a variable that points at your collection mid iteration in a foreach loop is valid code (modifying the collection, such as a list, will most often throw exceptions though, like Bunny83 points out).

But it’s not readable.

You wanted to know what the problem with the code is, anything that would make it unreliable. And yes… it makes it a management nightmare as unless you explicitly know what the underlying logic is and how it works, it’s not obvious what is ACTUALLY happening.

Just like my car door… I could get in my car. But it required extra knowledge about the situation to understand how to get in my car.

addendum

So I have another anecdote for you.

When I was a young man my father would train my brother and I on how to operate the various equipment in our life (we grew up around my grandparents farm, and my dad was a truck driver, lots of things to learn). My brother would get frustrated when my dad would teach us the “long/right way” to do things. He’d complain why dad won’t just teach us the “easy way” since that’s the way we were going to do it anyways.

My dad would scold him for wanting shortcuts.

“I’m teaching you the right way, so when the shortcut fails… you have the right way to fall back on.”

Because that’s the thing about the shortcut, it only works incidentally. Your exploiting the coincidence that might be common place.

My brother never listened though and time and time again he’d smash up his load, or get lost down some road because of low limbs, and other things.

95% of the time he was a great driver… but that 5%… things fall apart because he didn’t actually understand the fundamentals.

You should take the time to understand why your code does the things they do and how to do them the right way.

Heck, when you know the right way, you can come up with exploits for it. And usually if I did something like that, I’d comment my code explaining WHY I’m doing this weird exploit and why it just happens to work and how it benefits me to do it this way over the “right” way.

But… if you’re on a forum asking what might be wrong about your weird code choice. Well… that suggests you don’t actually know what the code is doing and you’re just relying on the coincidence that it happens to result in what you expect.

You’re asking to be stuck down a one way street with a low bridge in front of you.

If that’s acceptable for you then you could use a recursive version of your code:

testArray(new int[] { 1, 2, 3, 4, 5 });

void testArray(int[] testArr)
{
    foreach (int item in testArr)
    {
        if (item == 3)
        {
            testArray(new int[] { -1, -2 });
        }
        Debug.Log(item);
    }

}

However the order will be different:

1
2
-1
-2
3
4
5

These two statements:

… really summarize how I feel about so many “clever programmer tricks.”

I’ve seen them, I’ve done them, I try never to do them now.

The longer you program the more you realize that you are your own worst enemy.

The only defense I can find against this is to make everything simple, simple, simple simple, and then even a little more simple than that.

And even then, you’ll still surprise your future self.

I asked the question here to understand what is happening to prevent unreliable results and to understand what is happening and be sure it is no coincidence because I noticed the code has a severe smell.

I am in my first game as a hobby solo indiedev and that solo part is not changing. While my game grew I noticed if I try to make everything the way it should be, I will never get even close to finishing. Alone finding out how to do stuff correctly when you are alone and never before programmed a game is a nightmare.

But nevertheless: I absolutely understand what you are saying and I very much agree with you, because that exactly is, what I preached to my teams a long time ago while working in another field. No shortcuts and be sure to understand what you are doing.

Thanks for all the input.

Amen. Where Lord’s analogy diverges from software is that “shortcuts are good, but understand what is happening.”

And that keys into simple-simple: simple means shortcuts. But… understand them!

If you really want to blow your mind, just tell ChatGPT4 what you’re doing. It’s… surreal.

I like this guy’s reductivist approach: just keep asking again and again, “Can I…?” and build up your knowledge web step by step.

And don’t be afraid to redo some thing you did yesterday to prove you actually do “get” it.

Imphenzia: How Did I Learn To Make Games: