What changed in .net 5 that makes it not throw when changing dictionary values in foreach

31

In .NET<5 and .NET Core 3.1 the following code

var d = new Dictionary<string, int> { { "a", 0 }, { "b", 0 }, { "c", 0 } };
foreach (var k in d.Keys) 
{
   d[k]+=1;
}

throws

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

When targeting .NET 5 the snippet no longer throws.

What has changed?

I failed to find the answer in Breaking changes in .NET 5 and Performance Improvements in .NET 5.

Is it something to do with ref readonly T?

Share
Improve this question
3
  • I think we should be able to find the answer in the source code to Dictionary<, >, starting reading from the set accessor of the indexer public TValue this[TKey key] { /* ... */ }. There should exist a private field that keeps track on whether the collection was modified or not. If I assign with the setter d[k] = tmp; and k was previously in the Dictionary<,>, does that count as a modification? They must have changed that. – Jeppe Stig Nielsen Apr 4 at 9:56
  • In the old version, they do entries[i].value = value; version++; return; in the branch where we have an update. The version++ ensures the enumerator will blow up with an exception on the next MoveNext() on the enumerator. – Jeppe Stig Nielsen Apr 4 at 10:02
  • (They also allow it with the value collection, for example foreach (var v in d.Values) { Console.WriteLine("Top " + v + " " + d["c"]); d["c"] += 1; Console.WriteLine("Bottom " + v + " " + d["c"]); }. So I guess there is still only one version field for both keys and values. Where do I see the new C# source of public class Dictionary<TKey, TValue>?) – Jeppe Stig Nielsen Apr 4 at 10:23

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

Chart JS +ng2-charts not working on Angular+2