stepping through a collection in a foreach loop

D

David C

I posted this question, and from the replies, I get the impression that
I worded my posting very poorly, so let me try this again.

While debugging and stepping through this foreach loop

foreach(Student s in course.Students)
{
Console.WriteLine(s.StudentID);
}

I get this error:
******************************
An unhandled exception of type 'System.InvalidOperationException'
occurred in mscorlib.dll

Additional information: Collection was modified; enumeration operation
may not execute.
*******************************

Please note the following points

- This code RUNS FINE when run without debugging. NO error
- 2 of my friends can STEP THROUGH the foreach loop without running into
the error message with VS.NET 2003
- 2 other friends get the same error message again with VS.NET 2003
- I used to be able to step through fine with VS.NET 2003. This
instance of VS.NET 2003 is new. Never had a problem stepping through
before on my old machine.

Now let me show you the implentation of the course.Students property.
Please note that following code is just to demonstrate the concept, so
refrain from commenting on my style.

public ArrayList Students
{
get
{
if (students == null)
students = new ArrayList();

--here a call is made to the database to refresh
refreshStudentsFromDataBase();
return students;
}
}

I want it this way because course.Students can change, so everytime it's
called, I want it refreshed.

My understanding is that once course.Students is in a foreach loop, it
will not be called again. And when the code is run without debug, it
confirms my observation. But when I step through, I get the error. 2
people who ran the same code on their machine CAN STEP THROUGH.

Thanks.
 
J

Jon Skeet [C# MVP]

David C said:
I posted this question, and from the replies, I get the impression that
I worded my posting very poorly, so let me try this again.

<snip>

I suspect that while you're in the debugger, it executes the Students
property accessor. That changes the collection, hence the problem.

The most obvious solution to this is *not* to use a property to
populate the collection - at least not if it's already populated.
 
D

David C

I suspect that while you're in the debugger,
it executes the Students
property accessor. That changes the collection,
hence the problem.

The biggest issue is why the debugger behaves this way for some
instances of VS.NET and not for others.
 
G

gmiley

You could also copy the contents of course.Students to a new array and
foreach() that instead. Then you can keep your refresh code in the
property, but ensure that it doesn't cause any problems.

As to why some people experience the same problem and some don't, it
could be that your debugging settings are different perhaps? Maybe with
variable evaluation or something?
 
J

Jon Skeet [C# MVP]

David C said:
The biggest issue is why the debugger behaves this way for some
instances of VS.NET and not for others.

It may well depend on which tab the user happens to have visible.
 
D

David C

It may well depend on which tab the user
happens to have visible.

YOU WERE RIGHT!!!!

It is the autos tab that was causing the problem.

Now if somebody can explain why that tab is causing the problem and
whether Microsoft intended it that way and why....
 
B

Bruce Wood

Wow. Good call. I would never have thought of that. Of course the
debugger calls your own properties to populate its displays, so just
looking at the property in the debugger while in the loop would crash
the loop.
 
J

Jon Skeet [C# MVP]

David C said:
YOU WERE RIGHT!!!!

It is the autos tab that was causing the problem.

Now if somebody can explain why that tab is causing the problem

That's easy - when you execute a statement, the Autos tab needs to keep
you up to date on what the values you're looking at are - so it
executes properties...
and whether Microsoft intended it that way and why....

Microsoft didn't intend you to write properties which had the sort of
quantum property that observing the value also changed it.
 
G

Guest

as I've explained to you last time this was posted, for debugger to display
your fields and properties, it has to invoke it.
 
G

Guest

That's easy - when you execute a statement, the Autos tab needs to keep
you up to date on what the values you're looking at are - so it
executes properties...


Microsoft didn't intend you to write properties which had the sort of
quantum property that observing the value also changed it.

actually, I have lots of quantum properties (lazy instantiation for example).
 
J

Jon Skeet [C# MVP]

Daniel Jin said:
actually, I have lots of quantum properties (lazy instantiation for example).

Yes, that's fair enough - but I would hope that the values don't change
*after the initial "view"* on subsequent views.
 
C

Christoph Nahr

You could also copy the contents of course.Students to a new array and
foreach() that instead. Then you can keep your refresh code in the
property, but ensure that it doesn't cause any problems.

You don't need a copy, just declare a new variable of the same type
and assign the property to the variable before enumeration:

ArrayList students = course.Students;
foreach(Student s in students)
Console.WriteLine(s.StudentID);

That way your property access happens outside the foreach and you
should be fine.
 
J

Jon Skeet [C# MVP]

Christoph Nahr said:
You don't need a copy, just declare a new variable of the same type
and assign the property to the variable before enumeration:

ArrayList students = course.Students;
foreach(Student s in students)
Console.WriteLine(s.StudentID);

That way your property access happens outside the foreach and you
should be fine.

No, that's not the problem - the Students property was only being
evaluated once in the foreach loop itself anyway. The problem was that
the debugger was running the Students properly independently. That was
modifying the returned ArrayList, so there'd be the same problem with
the code above.
 
C

Christoph Nahr

No, that's not the problem - the Students property was only being
evaluated once in the foreach loop itself anyway. The problem was that
the debugger was running the Students properly independently. That was
modifying the returned ArrayList, so there'd be the same problem with
the code above.

Oh, you're right -- the property is changing the contents of the
already allocated array. True, you'd really need an array copy to
avoid the debugger hangup.
 
D

David C

Microsoft didn't intend you to write
properties which had the sort of quantum property that >observing the
value also changed it.

So what exactly is the right way of doing this?

Let's take Course.Students as an example. The number of Students can
change from one minute to next as they enroll or drop the course.

So I want the Course.Students property to be refreshed from the database
everytime it is referenced.

What is wrong with that approach. Why should Course.Students be
expected to stay static over the life time?
 
S

Scott Roberts

What is wrong with that approach.

Well, the problem you reported for one thing. ;)
Why should Course.Students be
expected to stay static over the life time?

I wouldn't expect it to stay static over the life time, but I would want
control over when it was refreshed instead of having it in the accessor.
What happens if you write some code that accesses the property in a tight
loop?
 
F

Fred Mellender

I don't see anything wrong with your approach. It is just that the debugger
gets in the way, as if two threads were updating the same object without
using locking. Why not code up a method, "getStudents()", and use that in
your code, replacing the "get" accessor. Then the debugger will be happy.

Alternatively, there might be some way to change the "get" accessor during
debugging, via #if DEBUG logic. But that would probably be too much of a
change between the debug and production code to be useful.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top