10

I declared a Dictionary type object and try to add some items into it. But I cannot modify even the value of the item. It's reasonable that key should not be modifiable, but why not the value? Thanks.

    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("1", "bob");
    dict.Add("2", "jack");
    dict.Add("3", "wtf");
    foreach (string key in dict.Keys)
    {
        dict[key] = "changed"; //System.InvalidOperationException: Collection was modified
    }
smwikipedia
  • 61,609
  • 92
  • 309
  • 482

10 Answers10

18

You are not allowed to modify the collection you are iterating over in a foreach loop. This has nothing to do with the dictionary - dict[key] = "value"; works perfectly fine outside foreach loops.

Femaref
  • 60,705
  • 7
  • 138
  • 176
15

The dictionary itself is not immutable. But you cannot change the dictionary while enumerating it in the foreach-loop.

Here a link for further reading: Why is The Iteration Variable in a C# foreach statement read-only?

Community
  • 1
  • 1
PetPaulsen
  • 3,442
  • 2
  • 22
  • 33
  • 2
    In other words, a collection currently iterated is immutable – abatishchev Nov 15 '10 at 10:55
  • 2
    @abatishchev, there is a difference between an object that should not be modified, and an object that can not be modified. The term _immutable_ refers to the latter, whereas a collection that is being iterated is the former. – Drew Noakes May 21 '13 at 14:06
7

You're using the term immutable wrong. It's commonly used for objects which internal state won't change once they are initialized. The Dictionary<TKey, TValue> is mutable as can be. That it throws exceptions when someone tries to modify it during enumeration is an explicitely implemented behaviour.

You're probably confused why the enumeration would change when you only change a value. This isn't covered by the other answers yet.

Well, when you do

dict[key] = "something";

the enumeration will change if the key does not yet exist, because it will be added in that case, causing the enumeration to change. Surely, the dictionary implementation could check that first and allow modification if the key already exists, but I guess it follows the principle Fail early, fail often to improve over-all integrity of applications.

herzmeister
  • 11,101
  • 2
  • 41
  • 51
4

It is not immutable, you can change it, it is just that the current iterator becomes invalid if you modify the collection. You can "solve" this by forcing the iterator to complete before you modify the collection

foreach (string key in new List<string>(dict.Keys))
{
    dict[key] = "changed"; //System.InvalidOperationException: Collection was modified
}
Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
2

Dictionary is not immutable (first, if it were, you would not be able to Add to it).

The problem is that you are trying to modify the collection while iterating over it - you cannot change a collection within a foreach block using it.

Changing values outside a foreach will work.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • You can't access a dictionary using an `int` index, unless it's actually a `Dictionary`. – LukeH Nov 15 '10 at 10:22
2

An analogy I can think of is trying to cut a tree branch while you are sitting on it. So its considered unsafe.

An alternative could be to use the for loop or clone the list.

ganeshran
  • 3,512
  • 7
  • 41
  • 69
1

You are trying to modify the collection at the same time you are iterating on it. That is not allowed. The exception is thrown by the move next method of the enumerator in foreach loop.

Unmesh Kondolikar
  • 9,256
  • 4
  • 38
  • 51
1

As @Femaref mentioned, you cannot change the dictionary while iterating inside a foreach loop. Another work around is to use for loop and use the following code to change it

for (int i = 0; i < dict.Count; i++)
{
    var key = dict.Keys.ElementAt(i);
    dict[key] = "changed";
}

Note: You have to use LINQ for this.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Anindya Chatterjee
  • 5,824
  • 13
  • 58
  • 82
1

Convert dict.Keys to array.

Then do that for the loop.

If you modify collection it will throw an exception.

Bleeding Fingers
  • 6,993
  • 7
  • 46
  • 74
curiosity
  • 1,207
  • 5
  • 23
  • 34
1

Microsoft decided that iEnumerable objects which support a Reset method should return exactly the same data if there are multiple passes made through them. This is a useful guarantee if one wishes to e.g. produce a string containing all the items (a first pass could tally up the length of all the items, and a second pass could concatenate them). Since even changing the value of a dictionary item would violate that guarantee, it is forbidden. It might have been nice to have changes to a dictionary value only cause a squawk if one in fact attempts a second pass through a collection, but that's not how Microsoft designed things. Also, I'm not familiar with the dictionary's internal implementation, but it's possible that it handles a Value change as a deletion and insertion. If it does that, that would certainly break an enumerator.

supercat
  • 77,689
  • 9
  • 166
  • 211