Andre S. Veteran Posted February 1, 2010 Veteran Share Posted February 1, 2010 Hi, I have say a Dictionary<Color, int> myDict; And I want to do this: foreach (var kvPair in myDict) { kvPair.Value = 0; // not necessarily always 0, could be calculated per-value, whatever } But this triggers a compiler error: Property or indexer 'System.Collections.Generic.KeyValuePair<Color,int>.Value' cannot be assigned to -- it is read only How can I do this? Link to comment Share on other sites More sharing options...
0 Calum Veteran Posted February 1, 2010 Veteran Share Posted February 1, 2010 Does it work if you try the following? foreach (KeyValuePair kvPair in myDict) { kvPair.Value = 0; // not necessarily always 0, could be calculated per-value, whatever } Link to comment Share on other sites More sharing options...
0 +chorpeac MVC Posted February 1, 2010 MVC Share Posted February 1, 2010 (edited) You can't alter a collection in a foreach loop, convert it to a normal for loop. *Nevermind* that would be adding/removing the collection item... for(int i=0; i< myDict.Values; i++) { kvPair.... } Link to comment Share on other sites More sharing options...
0 Andre S. Veteran Posted February 1, 2010 Author Veteran Share Posted February 1, 2010 Does it work if you try the following? foreach (KeyValuePair kvPair in myDict) { kvPair.Value = 0; // not necessarily always 0, could be calculated per-value, whatever } C# will infer System.Collections.Generic.KeyValuePair if you put var, so it's equivalent.Actually I know of a way, but it's ugly and wastes CPU cycles: foreach (Color color in Enum.GetValues(typeof(Color))) { myDict[color] = 0; } I'm wondering if I should use another container or if there's a more idiomatic way. Link to comment Share on other sites More sharing options...
0 +Majesticmerc MVC Posted February 1, 2010 MVC Share Posted February 1, 2010 C# will infer System.Collections.Generic.KeyValuePair if you put var, so it's equivalent. Actually I know of a way, but it's ugly and wastes CPU cycles: foreach (Color color in Enum.GetValues(typeof(Color))) { myDict[color] = 0; } I'm wondering if I should use another container or if there's a more idiomatic way. Not using KVPs I don't think, since KVP objects are basically read-only once they have been instantiated for reasons which are beyond me (Why make the value member read-only?). I was going suggest using a SortedDictionary object since they're implemented as a binary tree and you could use a depth-first search to basically loop through the whole collection, but the OrderedDictionary object doesn't provide the facility to do it. I was also going to suggest foreach'ing through the "Keys" collection, but I suspect that that would be less efficient than what you suggested. I would say that it would be easier just to take the performance hit in this case, since the lookup time for a Dictionary is close to O(1) since its implemented as a hash table. Unless its a completely performance critical piece of code, in which case you might need to look into some of the other collection types. Link to comment Share on other sites More sharing options...
0 Antaris Veteran Posted February 1, 2010 Veteran Share Posted February 1, 2010 As previously stated, KeyValuePair<K, V> is a structure (read: not class) with read-only properties. The reasons behind this is that structures perform better when they are immutable. It's better to instantiate a new instance of a KeyValuePair<K, V> then to try and modify it's content. That being said, you can't modify an enumeration, this is also be design, so: foreach (var pair in myDict) { pair.Value = 0; // This will fail because of read-only properties. } foreach (var pair in myDict) { pair = new KeyValuePair<...> // Won't work as you are modifying a read-only variable. } Whereas: foreach (Color key in myDict.Keys) { myDict[key] = 0; // Make the modification here } ... should work, as the you are enumerating over a collection of keys, not KeyValuePair<Color, int> items, and can modify the contents of the dictionary correctly. Rob, +Majesticmerc and Andre S. 3 Share Link to comment Share on other sites More sharing options...
0 +Majesticmerc MVC Posted February 1, 2010 MVC Share Posted February 1, 2010 As previously stated, KeyValuePair<K, V> is a structure (read: not class) with read-only properties. The reasons behind this is that structures perform better when they are immutable. And now I know! Thanks Antaris, have some Rep! :D Link to comment Share on other sites More sharing options...
0 Antaris Veteran Posted February 1, 2010 Veteran Share Posted February 1, 2010 Lol, look at me, I now have some Rep :D, cheers :) Link to comment Share on other sites More sharing options...
0 Calum Veteran Posted February 1, 2010 Veteran Share Posted February 1, 2010 C# will infer System.Collections.Generic.KeyValuePair if you put var, so it's equivalent. [...] Ahh, thank you for explaining :happy: I can't believe I didn't know that. Link to comment Share on other sites More sharing options...
0 Andre S. Veteran Posted February 1, 2010 Author Veteran Share Posted February 1, 2010 Thanks for the explanation Antaris, I totally forgot about myDict.Keys. Link to comment Share on other sites More sharing options...
0 Andre S. Veteran Posted February 1, 2010 Author Veteran Share Posted February 1, 2010 Whereas: foreach (Color key in myDict.Keys) { myDict[key] = 0; // Make the modification here } ... should work, as the you are enumerating over a collection of keys, not KeyValuePair<Color, int> items, and can modify the contents of the dictionary correctly. Well, I'm afraid it doesn't, after testing it, I get a runtime error on the second iteration saying it cannot continue because the collection has been modified. So, back to iterating over Enum.GetValues for now. :unsure: Link to comment Share on other sites More sharing options...
0 Calculator Posted February 1, 2010 Share Posted February 1, 2010 Well, I'm afraid it doesn't, after testing it, I get a runtime error on the second iteration saying it cannot continue because the collection has been modified. So, back to iterating over Enum.GetValues for now. :unsure: You get an error because the Keys collection is invalid after changing the value. Now, you can get around this by iterating over a list which temporarily holds the keys. That way, you get a List<Color> which contains all keys at that certain point. This list is not managed by the Dictionary and therefore won't be invalidated when you make changes to the Dictionary. foreach (Color key in myDict.Keys.ToList()) { myDict[key] = 0; // Make the modification here } This appears to be the fastest and safest way to do this, so go and make good use of it. :) Link to comment Share on other sites More sharing options...
0 +Majesticmerc MVC Posted February 1, 2010 MVC Share Posted February 1, 2010 foreach (Color key in myDict.Keys.ToList()) { myDict[key] = 0; // Make the modification here } This appears to be the fastest and safest way to do this, so go and make good use of it. :) So are the keys cached in an array (or equivalent) then? I figured that it wouldn't be, whereas the enum values would just be known, making it less safe, but faster. Am I way off then? Link to comment Share on other sites More sharing options...
0 Andre S. Veteran Posted February 1, 2010 Author Veteran Share Posted February 1, 2010 So are the keys cached in an array (or equivalent) then? I figured that it wouldn't be, whereas the enum values would just be known, making it less safe, but faster. Am I way off then? Enum.GetValues() creates an array, while myDict.Keys.ToList() creates a List. Either way, allocation of a container every time. I don't like either, but it seems there's no other way.It's a bit silly, but anyhow if I was writing for ideal performance I'd be using C++ so I guess it's tolerable. Link to comment Share on other sites More sharing options...
0 Antaris Veteran Posted February 2, 2010 Veteran Share Posted February 2, 2010 I didn't realise the dictionary worked that way, the enumerable of keys must also be bound to stop underlying collection changes (which is expected of an enumerable). Link to comment Share on other sites More sharing options...
Question
Andre S. Veteran
Hi, I have say a
Dictionary<Color, int> myDict;
And I want to do this:
foreach (var kvPair in myDict) { kvPair.Value = 0; // not necessarily always 0, could be calculated per-value, whatever }But this triggers a compiler error:
Property or indexer 'System.Collections.Generic.KeyValuePair<Color,int>.Value' cannot be assigned to -- it is read only
How can I do this?
Link to comment
Share on other sites
14 answers to this question
Recommended Posts