I have a bug in foreach loop

  • Thread starter Thread starter Alvin Bruney
  • Start date Start date
A

Alvin Bruney

Error:
Collection was modified; enumeration operation may not execute.

I've dodged this issue for a while now with workarounds but now i want to
stand up and fight.
I don't want to run away from it anymore.
How do I get around tampering with a collection within a loop and keep
iterating. Here is my code:

//Threadwork.Getlist returns a hash table
foreach(DictionaryEntry key in ThreadWork.GetList)
{
lock(ThreadWork.GetList)
ThreadWork.GetList.Remove(key.Key.ToString());
}

I know why it is happening, but I don't know how to fix it. I've had the
same issue with operations like this
foreach(listitem li in somelistbox.items)
{
trying to remove 2 li items in succession will throw the same exception
because the collection is modified.
}

I know how to work around it by using an arraylist as a temporary container
but I don't want a work around anymore. I want to fix it. I want to remove
the item and keep iterating without using a secondary container to store the
values. I think that workaround is ineligant, cheap and doesn't promote
robust coding. Yuck!

I'd like some help please.
 
You can use for loops instead
for(int x = 0; x < list.Count; x++)
{
object o = list[x];
// if o should be removed
{
list.Remove(o);
x--; // to check the same x again since this spot will be filled by
"x+1" and so on
}
}
 
Morten Wennevik said:
You can use for loops instead
for(int x = 0; x < list.Count; x++)
{
object o = list[x];
// if o should be removed
{
list.Remove(o);
x--; // to check the same x again since this spot will be filled by
"x+1" and so on
}
}

Another alternative is to iterate from the other end:

for (int x=list.Count-1; x >=0 ; x--)
{
object o = list[x];
// if o should be removed
{
list.RemoveAt(x);
}
}

Note that I've changed list.Remove(o) to list.RemoveAt(x) as that way
the list doesn't need to search for the object to remove and it means
that the *exact* element will be removed rather than one which is equal
and potentially earlier in the list.
 
So I guess there is no way to iterate thru a collection and tamper with it
then?
I'm not complaining but it seems like if they can implement edit and
continue, they could have implemented this.

I know this solution, I've used it but I was kinda hoping, praying that the
foreach version was available somehow.

Thanks anyway

--


-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
You can use for loops instead
for(int x = 0; x < list.Count; x++)
{
object o = list[x];
// if o should be removed
{
list.Remove(o);
x--; // to check the same x again since this spot will be filled by
"x+1" and so on
}
}
 
yes, but that is really only a work around. There seems to be no way to edit
the collection while iterating thru it since it is read only. We had that
ability in C++ by the way. I'm not starting a new thread here. just saying
that i'll stash that quirk away in the archives of .net faux pas.

--


-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
Jon Skeet said:
Morten Wennevik said:
You can use for loops instead
for(int x = 0; x < list.Count; x++)
{
object o = list[x];
// if o should be removed
{
list.Remove(o);
x--; // to check the same x again since this spot will be filled by
"x+1" and so on
}
}

Another alternative is to iterate from the other end:

for (int x=list.Count-1; x >=0 ; x--)
{
object o = list[x];
// if o should be removed
{
list.RemoveAt(x);
}
}

Note that I've changed list.Remove(o) to list.RemoveAt(x) as that way
the list doesn't need to search for the object to remove and it means
that the *exact* element will be removed rather than one which is equal
and potentially earlier in the list.
 
I think I lost something converting the foreach to a for loop. I can no
longer correctly obtain the hash code key thru casting. It's bugging me out.

The previous code
foreach(DictionaryEntry key in ThreadWork.GetList) retrieved a hashtable.
The foreach automatically

presented me with the index key for that hashtable. Each key is an index for
an array object of length 19. So i could do this inside the loop.

object[] values = (object[]) ThreadWork.GetList[key.Key.ToString()]; to
return the exact object and modify it accordingly

string username o= values[4].ToString() for example, since this array object
contains a lot of user info about the user logged into the system. Well the
for loop took that ability away from me. I need to get it back.

for(int x = 0; x < list.Count; x++)
{
object o = list[x];

returns a null object. I hope i am making sense here.


//relevant declaration
Hashtable AccessTable = new Hashtable()

[snip]

object[] valTable = new object[]{

"Server: "+HttpContext.Current.Server.MachineName, ...[snip]

[snip]

AccessTable.Add(si,valTable);
 
Alvin Bruney said:
yes, but that is really only a work around. There seems to be no way to edit
the collection while iterating thru it since it is read only. We had that
ability in C++ by the way. I'm not starting a new thread here. just saying
that i'll stash that quirk away in the archives of .net faux pas.

You may find this useful:

http://msdn.microsoft.com/library/default.asp?url=/library/enus/dncscol/html/csharp01212002.asp

Scroll down to the section headed "Isolation".
 
Turns out this isn't as easy as it seems. I've found a very long thread with
this. The cure was

either to put a catch block and have it iterate thru the exception, Yuck!

Use a temporary array to store the items. I couldn't get that to work
because the copyto function kept saying it couldn't copy the hashtable keys
to the system array

Use an Isolator class written by eric gunnerson to sit between the iterator
and the items.

Each method brings a lot of pain. Yuck.

I'd like the development team to look into this because it is a problem. It
comes up often enough to warrant a better fix. One example is removing items
from a listbox. This happens frequently enough in a gui application but is
difficult to implement because of the readonly restriction on the list.

regards

--


-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
Alvin Bruney said:
Still need help here. This isn't working.
Anybody?

--


-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
Alvin Bruney said:
I think I lost something converting the foreach to a for loop. I can no
longer correctly obtain the hash code key thru casting. It's bugging me out.

The previous code
foreach(DictionaryEntry key in ThreadWork.GetList) retrieved a hashtable.
The foreach automatically

presented me with the index key for that hashtable. Each key is an index for
an array object of length 19. So i could do this inside the loop.

object[] values = (object[]) ThreadWork.GetList[key.Key.ToString()]; to
return the exact object and modify it accordingly

string username o= values[4].ToString() for example, since this array object
contains a lot of user info about the user logged into the system. Well the
for loop took that ability away from me. I need to get it back.

for(int x = 0; x < list.Count; x++)
{
object o = list[x];

returns a null object. I hope i am making sense here.


//relevant declaration
Hashtable AccessTable = new Hashtable()

[snip]

object[] valTable = new object[]{

"Server: "+HttpContext.Current.Server.MachineName, ...[snip]

[snip]

AccessTable.Add(si,valTable);
 
Alvin Bruney said:
I think I lost something converting the foreach to a for loop. I can no
longer correctly obtain the hash code key thru casting. It's bugging me out.

The previous code
foreach(DictionaryEntry key in ThreadWork.GetList) retrieved a hashtable.
The foreach automatically

presented me with the index key for that hashtable. Each key is an index for
an array object of length 19. So i could do this inside the loop.

object[] values = (object[]) ThreadWork.GetList[key.Key.ToString()]; to
return the exact object and modify it accordingly

You could just have done:

object[] values = (object[]) key.Value;

by the way...
string username o= values[4].ToString() for example, since this array object
contains a lot of user info about the user logged into the system. Well the
for loop took that ability away from me. I need to get it back.

for(int x = 0; x < list.Count; x++)
{
object o = list[x];

returns a null object. I hope i am making sense here.

Not a lot, I'm afraid - could you provide a short but complete example
which demonstrates it? Using a for loop shouldn't make it any harder to
get at the information...
 
Using a for loop shouldn't make it any harder to
get at the information...

Never mind, I've fixed it after some grief. The for loop iteration index
returns a hashtable. That wasn't what i wanted. The foreach iteration
returns me a key into that hash table. I was unable to go from point A to
point B, since the the value attached to the object was an array, I couldn't
get to the array. Anyway both avenues lead down a troubled road because the
bottom line is the collection is readonly. I'm not at all satisified with
this limitation in C#. It needs to be addressed.

vapor
--


-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
Jon Skeet said:
Alvin Bruney said:
I think I lost something converting the foreach to a for loop. I can no
longer correctly obtain the hash code key thru casting. It's bugging me out.

The previous code
foreach(DictionaryEntry key in ThreadWork.GetList) retrieved a hashtable.
The foreach automatically

presented me with the index key for that hashtable. Each key is an index for
an array object of length 19. So i could do this inside the loop.

object[] values = (object[]) ThreadWork.GetList[key.Key.ToString()]; to
return the exact object and modify it accordingly

You could just have done:

object[] values = (object[]) key.Value;

by the way...
string username o= values[4].ToString() for example, since this array object
contains a lot of user info about the user logged into the system. Well the
for loop took that ability away from me. I need to get it back.

for(int x = 0; x < list.Count; x++)
{
object o = list[x];

returns a null object. I hope i am making sense here.

Not a lot, I'm afraid - could you provide a short but complete example
which demonstrates it? Using a for loop shouldn't make it any harder to
get at the information...
 
Back
Top