c#: Changing a value in a dictionary in a foreach
I was trying to loop through a dictionary,
update it's value if a certain condition was met,
if not remove the item from the dictionary.
(Seems I have done something lke this before, but that's why I blog it, so I can remember next time… :))
Here's my first attempt:
private void FindEditedFieldsNotWorking(Dictionary<string, string> dictionary, Control ctrl)
{
foreach (KeyValuePair<string,string> kvp in dictionary)
{
CheckBox c = ctrl.Controls["chk" + kvp.Key] as CheckBox;
if (c != null && c.Checked)
{
dictionary[kvp.Key] = ctrl.Controls[kvp.Key].Text;
}
else
dictionary.Remove(kvp.Key);
}
}
It compiles ok and seems ok.
Might have a misgiving with removing the item in a loop, but hey it's worth a shot..
When running this I got an exception something like
"Collection was modified; enumeration operation may not execute."
My first thought was that my dictionary.Remove was the culprit, so I put a breakpoint there.
But that was not the case (although that is not supported either in this loop)
It seems that I can't actually change the freakin' value within my foreach loop…!
So I tried the same but this time using just the keys in the loop:
private void FindEditedFieldsNotWorkingEither(Dictionary<string, string> dictionary, Control ctrl)
{
foreach (string Key in dictionary.Keys)
{
CheckBox c = ctrl.Controls["chk" + Key] as CheckBox;
if (c != null && c.Checked)
{
dictionary[Key] = ctrl.Controls[Key].Text;
}
else
dictionary.Remove(Key);
}
}
Still no go, same error.
Eventually I read a little bit about this using google and found that I couldn't do any changes while enumerating.
Here are some links I used:
http://stackoverflow.com/questions/1070766/editing-dictionary-values-in-a-foreach-loop
Seems like a bad design decision has made this a problem for many ppl.
The incling is probably the implisit add functionality when doing dict[key]=value; when key is not existing.
Anyways we have to get around this somehow.
The solution I thought was nicest when I couldn't use the "logic" solution is:
.Net 2.0
private void FindEditedFieldsWorking(Dictionary<string, string> dictionary, Control ctrl)
{
List<string> keys = new List<string>(dictionary.Keys);
foreach (string key in keys)
{
CheckBox c = ctrl.Controls["chk" + key] as CheckBox;
if (c != null && c.Checked)
{
dictionary[key] = ctrl.Controls[key].Text;
}
else
dictionary.Remove(key);
}
}
Almost as nice as the first version, and with the added premium that dictionary.Remove won't be a problem.
Or if you're running .Net 3.0 or higher you could just do this:
private void FindEditedFieldsWorking(Dictionary<string, string> dictionary, Control ctrl)
{
foreach (var key in dictionary.Keys.ToList())
{
CheckBox c = ctrl.Controls["chk" + key] as CheckBox;
if (c != null && c.Checked)
{
dictionary[key] = ctrl.Controls[key].Text;
}
else
dictionary.Remove(key);
}
}
where the ToList does the same as our 2.0 solution, namely creating a temp list to hold the keys.
Nice solution on a not so nice problem.
Hope this helps someone,
it will help me next time anyways :)
Regards
Henri Merkesdal
MERIT