tags:

views:

243

answers:

4
+5  Q: 

Looping question

When you do stuff like:

for (int i = 0; i < collection.Count; ++i )

is collection.Count called on every iteration?

Would the result change if the Count property dynamically gets the count on call?

+10  A: 

Count would be evaluated on every pass. If you continued to add to the collection and the iterator never caught up, you would have an endless loop.

class Program
    {
        static void Main(string[] args)
        {
            List<int> intCollection = new List<int>();
            for(int i=-1;i < intCollection.Count;i++)
            {
                intCollection.Add(i + 1);
            }
        }
    }

This eventually will get an out of memory exception.

Jeff Martin
Doesn't C# stop you from modifying a collection while iterating through it? Or does that only happen in for each loops?
jeffamaphone
AFAIK only "for" foreach.
Joan Venge
That only happens when you use Enumerators
dustyburwell
Which includes "foreach"
dustyburwell
@jeffamaphone - I thought the same thing, but wrote a small program that demonostrated it doesn't. Interesting.
Jay Riggs
The OP doesn't say what collection is, but I used a List<> for my test.
Jay Riggs
hrm - two down votes, no reason why?
Jeff Martin
+2  A: 

Yes count is checked at every call from the first iteration after the initialization of i to the last iteration where the check fails and the for loop is exited. You can modify the collections count if you want but realize you could end up in an endless loop.

Kelsey
+5  A: 

Yes Count will be evaluated on every single pass. The reason why is that it's possible for the collection to be modified during the execution of a loop. Given the loop structure the variable i should represent a valid index into the collection during an iteration. If the check was not done on every loop then this is not provably true. Example case

for ( int i = 0; i < collection.Count; i++ ) {
  collection.Clear();
}

The one exception to this rule is looping over an array where the constraint is the Length.

for ( int i = 0; i < someArray.Length; i++ ) {
  // Code
}

The CLR JIT will special case this type of loop, in certain circumstances, since the length of an array can't change. In those cases, bounds checking will only occur once.

Reference: http://blogs.msdn.com/brada/archive/2005/04/23/411321.aspx

JaredPar
What if I resize the array inside the loop?
Joan Venge
@Joan, array's can't resize
JaredPar
@JaredPar: No, but you can re-assign someArray
Henk Holterman
@Henk, yes that's part of the heuristic which determines if they can special case this loop.
JaredPar
@Jared: But Array has a static Resize method, right? That's what I meant, althoug probably assigning a new array. Also when you said "that's part of the heuristic", you mean the compiler recognizes this too?
Joan Venge
@Jaredpar, I'm starting to have second thoughts - aren't we confusing this with the bounds-check optimization? The Length property gets translated into a ldlen opcode, I'd call that inlining.
Henk Holterman
@Joan, yes there is a Resize but it creates a new array. Note the first parameter is taken by reference.
JaredPar
@Henk, it's a little bit of both. The JIT will only do a single bound check here in the optimized scenario. In the non-array scenario it will do N bound checks which require N property evals.
JaredPar
@Jared: Do you know why it's taken by ref? I don't know why it's defined like that?
Joan Venge
@Joan, it's because array's cannot be resized at all. It takes it byref because it makes a copy of the array and returns the copy back. So it only **appears** to resize the array.
JaredPar
I got it now. Thanks Jared.
Joan Venge
+1  A: 

Side note, this is NOT checked for every interation in VB.

Unlike C#, VB caches the result of the collection.Count.

EDIT:

The literal VB version of the C# for loop is:

Dim i = 0
Do While i < collection.Count
    'code goes here
    i+=1
Loop
Jonathan Allen
So is it broken if you change the collection while iterating?
Jon Skeet
Yeah I wonder this too.
Joan Venge
If you want to change the collection while looping, you need to use a loop.
Jonathan Allen