none
"Collection was modified; enumeration operation may not execute." error

    Question

  • Hello

    I have the following foreach loop:

    foreach (string variable in _variables.Keys)

    {

    double fx = ObtieneValorParametro(variable, poliza, periodo);

    if (_variables[variable] == null)

    _variables[variable] = fx;

    }

    Where _variables is of Dictionary<string, object> type.

    The only modification to the collection I'm making is to change the value of an element from null to a value, as you see. I'm not removing nor adding elements, so, why I receive that error? I tried by using a traditional for loop, but since the collection is a Dictionary<string, object> collection I couldn't access the elements by mean of their indices.

    Any help woul be greatly appreciated

    Thanks

    Jaime

    Thursday, April 27, 2006 8:54 PM

Answers

  • Jaime,

    The reason you see this is The IEnumerator returned by the underlying collection may expose Current property as read-only.Generally you should avoid changes to collections(in fact most cases you will not even be able to change the collection) using for--each.

    There is a small caveat to this. Consider the below example:

    You will see that the line in pink is allowed while the line in green is not. So I am actaully able to change the referenced object's value but not the actual reference itself.

    This makes absolute sense, as the collection just holds on to the address of the key and the value objects and does not really care what changes in their topology.

    Hopefully this helps you understand what is happenning.

    dict[key].name = key.ToLower();

    dict[key]= new Emp(key.ToLower());

    class Emp

    {

    public string name;

    public Emp(string n)

    {

    name = n;

    }

    }

    static void Main(string[] args)

    {

    Dictionary<string, Emp> dict = new Dictionary<string, Emp>();

    dict.Add("A", new Emp("A"));

    dict.Add("B", new Emp("B"));

    dict.Add("C", new Emp(""));

    dict.Add("D", new Emp(""));

    foreach (string key in dict.Keys)

    {

    if (dict[key].name==string.Empty)

    {

    try

    {

    //dict[key].name = key.ToLower();

    dict[key]= new Emp(key.ToLower());

    Console.WriteLine("Key " + key + ", Value :" + dict[key].name);

    }

    catch (InvalidOperationException ie)

    {

    Console.WriteLine(ie.Message);

    }

    }

    }

    Console.ReadLine();

    }

    Friday, April 28, 2006 1:34 AM
  • You are changing the contents of the list during enumeration which is the no-no being reported.

    A better way to handle would be to enumerate as you are, but keep second list of the "null" values.  When done, populate them as needed:

       List<string> nullVariables = new List<string>();
       foreach(....)
      {
          if (_var[var] == null) 
             secondList.Add(var); 
      }


       foreach(string item in secondList) 
        {
           _var[var] = Calculate(var);
       }

     

    Friday, April 28, 2006 12:45 AM

All replies

  • You are changing the contents of the list during enumeration which is the no-no being reported.

    A better way to handle would be to enumerate as you are, but keep second list of the "null" values.  When done, populate them as needed:

       List<string> nullVariables = new List<string>();
       foreach(....)
      {
          if (_var[var] == null) 
             secondList.Add(var); 
      }


       foreach(string item in secondList) 
        {
           _var[var] = Calculate(var);
       }

     

    Friday, April 28, 2006 12:45 AM
  • Jaime,

    The reason you see this is The IEnumerator returned by the underlying collection may expose Current property as read-only.Generally you should avoid changes to collections(in fact most cases you will not even be able to change the collection) using for--each.

    There is a small caveat to this. Consider the below example:

    You will see that the line in pink is allowed while the line in green is not. So I am actaully able to change the referenced object's value but not the actual reference itself.

    This makes absolute sense, as the collection just holds on to the address of the key and the value objects and does not really care what changes in their topology.

    Hopefully this helps you understand what is happenning.

    dict[key].name = key.ToLower();

    dict[key]= new Emp(key.ToLower());

    class Emp

    {

    public string name;

    public Emp(string n)

    {

    name = n;

    }

    }

    static void Main(string[] args)

    {

    Dictionary<string, Emp> dict = new Dictionary<string, Emp>();

    dict.Add("A", new Emp("A"));

    dict.Add("B", new Emp("B"));

    dict.Add("C", new Emp(""));

    dict.Add("D", new Emp(""));

    foreach (string key in dict.Keys)

    {

    if (dict[key].name==string.Empty)

    {

    try

    {

    //dict[key].name = key.ToLower();

    dict[key]= new Emp(key.ToLower());

    Console.WriteLine("Key " + key + ", Value :" + dict[key].name);

    }

    catch (InvalidOperationException ie)

    {

    Console.WriteLine(ie.Message);

    }

    }

    }

    Console.ReadLine();

    }

    Friday, April 28, 2006 1:34 AM
  • Or you could something like:

    private void Update<K>(Dictionary<K, float> dict, float sum)

    {

       foreach (K key in new List<float>(dict.Keys))

       {

           dict[key] = dict[key] / sum;

       }

    }

    • Proposed as answer by alien_student Saturday, June 25, 2011 4:46 AM
    Tuesday, January 09, 2007 10:29 PM
  • i have something similar here, related to ToolStripMenuItem, ToolStripItem, etc.

     

    please see the following code snippet/sample (using c# .net 2.0):

     

    void Test()
    {
      ToolStripMenuItem sourceMenu = new ToolStripMenuItem( "source_root" );
      sourceMenu.DropDownItems.Add( new ToolStripMenuItem( "source.A" ) );
      sourceMenu.DropDownItems.Add( new ToolStripMenuItem( "source.B" ) );

     

      ToolStripMenuItem targetMenu = new ToolStripMenuItem( "target_root" );
      foreach (ToolStripMenuItem menuItem in sourceMenu.DropDownItems)
      {
        System.Diagnostics.Debug.Write(
          "source: " + sourceMenu.DropDownItems.Count + "; " +
          "target: " + targetMenu.DropDownItems.Count );
        
        //the following will remove the item from the source!?

        //this leads to enumeration exception in foreach loop!
        targetMenu.DropDownItems.Add( menuItem );

       

        System.Diagnostics.Debug.Write(
          "source: " + sourceMenu.DropDownItems.Count + "; " +

          "target: " + targetMenu.DropDownItems.Count );

      }
    }


     

    any ideas?

    kind regards, philipp

     

    keywords: .net dotnet c# csharp foreach enumeration framework

    Tuesday, October 23, 2007 4:22 PM
  • This is the exact same problem as above, but indirectly - which is why it's not so obvious.  You are changing the contents of the list being enumerated.

     

    A ToolStripItem can only have one parent.  So when you call:

    targetMenu.DropDownItems.Add( menuItem );

     

    ...it is also resulting in an implicit call to:

    sourceMenu.DropDownItems.Remove( menuItem );

     

    The simplest way (in my opinion) is the same as the second suggestion above.  Create a List<ToolStripiMenuItem> - populate it as you go

     

    List<ToolStripMenuItem> newItems = new List<ToolStripMenuItem>();

      ToolStripMenuItem targetMenu = new ToolStripMenuItem( "target_root" );
      foreach (ToolStripMenuItem menuItem in sourceMenu.DropDownItems)
      {
        System.Diagnostics.Debug.Write(
          "source: " + sourceMenu.DropDownItems.Count + "; " +
          "target: " + targetMenu.DropDownItems.Count );
        

    newItems.Add( menuItem );

       

        System.Diagnostics.Debug.Write(
          "source: " + sourceMenu.DropDownItems.Count + "; " +

          "target: " + targetMenu.DropDownItems.Count );

      }

      foreach( ToolStripMenuItem newItem in newItems )

      {

    targetMenu.DropDownItems.Add(newItem);

      }
    }

    Tuesday, October 23, 2007 4:49 PM
  • thank you very much!

     

    i wasn't aware of the fact that the parent of the source is being changed.

    and in conclusion, that the DropDownItem will implicitly get removed from the source!

     

    i really don't like implicit (and thereby "invisible") conventions Wink

     

    by the way:

    in unmanaged / native code (c++), the visual studio has a debug option "new breakpoint: new DATA breakpoint".

    i was hoping to trace down who is removing that item from source and why.

    but unfortunately, these "data breakpoints" are not available for managed code (c#).

     

    is there something similar for managed code?

    i.e. automatically break when someone / something modifies the variable XYZ?

     

     kind regards, philipp

    Tuesday, October 23, 2007 5:00 PM
  • private void Update<K>(Dictionary<K, float> dict, float sum)

    {

       foreach (K key in new List<float>(dict.Keys))

       {

           dict[key] = dict[key] / sum;

       }

    }

     

    this is exact solution to add or remove some items from the collections.

    thanx to give exact solution.

     

    Dinesh Rathi

    • Proposed as answer by jlabrou Thursday, January 06, 2011 2:02 PM
    Monday, October 29, 2007 7:29 AM
  • Hello,

    I have a similar problem, but the difference is that I don't want to modify anything. The first version of my code was:

                    Principal miPadre = (Principal)this.MdiParent;
                    foreach (ToolStripItem toolStripItem in miPadre.MainMenuStrip.Items)
                    {                                     
                        if (toolStripItem.Text.CompareTo("Secciones") == 0)                       
                            toolStripItem.Enabled = false;
                    }

    The first day that I tried it, it works fine but then I got the error. Then just to test the code I made this:

                    Principal miPadre = (Principal)this.MdiParent;
                    foreach (ToolStripItem toolStripItem in miPadre.MainMenuStrip.Items)
                    {                   
                        MessageBox.Show(toolStripItem.Text);
                    }

    As you can see, I'm not modifying nothing and the error is still there. Any idea?


    Thank you very much,

    Kim
    Tuesday, January 22, 2008 5:39 PM
  • Hi,

     

    I hit the same error when trying to modify my dictionary. The easiest way i thought of to solve it was:

     

    Dictionary<MyKey, MyValue> temp = new Dictionary<MyKey, MyValue>();

     

    foreach (KeyValuePair<MyKey, MyValue> entry in m_MyDic)

    {

    temp.Add(entry.Key, AddRemove);

    }

    m_MyDic = temp;

     

     

    Hope this is helpful.

     

     

     

     

    Tuesday, May 20, 2008 3:07 AM
  • Another approach would be to use the collection as in below link

    http://www.codeproject.com/KB/cs/MultiMap_P_2.aspx
    • Proposed as answer by Dave Black Wednesday, May 18, 2011 12:30 PM
    • Unproposed as answer by Dave Black Saturday, June 25, 2011 2:58 PM
    Tuesday, March 10, 2009 9:18 PM
  • Hi,

       i have a collection, and when i modify this collection, i am also getting the InvalidOperation exception. I copied the collection items into a list and then modified and put it back in the original collection. i modiifed as below

    List

     

    <DiagramNode> items = new List<DiagramNode>(this.fpidrList[CurrentServoIndex].Nodes.Count);

     

    foreach (DiagramNode node in this.fpidrList[CurrentServoIndex].Nodes)

    {

    items.Add(node);

    }

     

    //this.fpidrList[CurrentServoIndex].Nodes.Clear();

     

    foreach (DiagramNode node in items)

    {

     

    BaseNode baseNode = node as BaseNode;

     

    if (null != baseNode)

    baseNode.SizeMode =

    this.currMode;

     

    this.fpidrList[CurrentServoIndex].Nodes.Add(baseNode);

    }

    Even then the InvalidOperation exception is coming. Can someone suggest what i did wrong in the above code.

    Regards,
    Bharath kumar.K

    • Proposed as answer by LeeDodd Tuesday, August 18, 2009 8:01 PM
    Tuesday, August 11, 2009 2:50 PM
  • I used reverse enumeration. Worked for me. (I was originally getting the same errors as reported...)

    for (int i = (_list.Items.Count - 1); i >= 0; i--)

      {

        if (Convert.ToDateTime(_list.Items[i]["ExpirationDate"]) <= DateTime.Now)

          {

            _list.Items[i].Delete();

            _list.Update();

            _web.Update();

          }

      }

    Wednesday, May 26, 2010 10:39 PM
  •  foreach (var jisUsSizeMapping in _recordsToUpdate)
     {


       if (tblRow2Update.ID == jisUsSizeMapping.ID)
      {
        _recordsToUpdate.Remove(jisUsSizeMapping);
        break; /*  Once you have modified your collection break out of the loop  */
      }


    }

    Wednesday, March 09, 2011 8:50 AM
  • I know this isn't the cause in your specific case but this same exact error can be caused by race conditions from poor thread-synchronization - one thread is enumerating. another modifies the collection.

    If this post is helpful, please mark it as such!
    Dave Black, MCPD, MCTS
    http://dave-black.blogspot.com

    • Proposed as answer by Ghouse Barq Monday, March 12, 2012 5:07 AM
    Wednesday, March 09, 2011 8:16 PM
  • Yes they are. They're just called breakpoints.
    Tuesday, May 17, 2011 11:20 AM
  • The enumeration is a read-only collection.  You can accomplish the same task by counting through the array:

    for (int count = 0;count < _variables.Keys.Length;count++)

    {

    string variable = _variables.Keys[count];

    if(_variables[variable] == null)

    {

    _variables[variable] = fx;

    }

    }


    Ron
    Friday, June 17, 2011 1:04 PM
  • Hey Meidan Alon thank you very much, that's it the solution that i've looking for... the solutions is in bold... new List<float>(dict.Keys).

    yeah..!!!!

    Saturday, June 25, 2011 4:49 AM
  • This is definitely the correct way to accomplish what you are trying to do. Just pass your enumerable collection into a new copy (a list in this example) in the foreach loop and then update the original list as needed. Creating multiple lists and maintaining them is a waste of resources and overly difficult to try and maintain. 

    Glade Mellor

    Tuesday, December 27, 2011 11:36 PM
  • Thank you California
    Monday, July 23, 2012 8:23 AM