在foreach循环中编辑字典值

我正在试图从字典中build立一个饼图。 在展示饼图之前,我想整理一下数据。 我正在删除所有饼图片的5%以内,并将它们放在“其他”饼图片中。 但是我收到了一个Collection was modified; enumeration operation may not execute Collection was modified; enumeration operation may not execute在运行时Collection was modified; enumeration operation may not executeexception。

我明白为什么在迭代他们的时候,你不能在词典中添加或删除项目。 不过,我不明白为什么你不能简单地在foreach循环中改变一个现有的键的值。

任何build议重新:修复我的代码,将不胜感激。

 Dictionary<string, int> colStates = new Dictionary<string,int>(); // ... // Some code to populate colStates dictionary // ... int OtherCount = 0; foreach(string key in colStates.Keys) { double Percent = colStates[key] / TotalCount; if (Percent < 0.05) { OtherCount += colStates[key]; colStates[key] = 0; } } colStates.Add("Other", OtherCount); 

在字典中设置一个值会更新其内部的“版本号” – 这会使迭代器和任何与键或值集合关联的迭代器失效。

我确实看到了你的观点,但同时如果值集合可能在中间迭代中改变,那么将会很奇怪 – 为了简单起见,只有一个版本号。

修正这种事情的正常方法是事先复制密钥集合并遍历副本,或者迭代原始集合,但是维护一系列更改,在完成迭代后将应用这些更改。

例如:

首先复制密钥

 List<string> keys = new List<string>(colStates.Keys); foreach(string key in keys) { double percent = colStates[key] / TotalCount; if (percent < 0.05) { OtherCount += colStates[key]; colStates[key] = 0; } } 

要么…

创build一个修改列表

 List<string> keysToNuke = new List<string>(); foreach(string key in colStates.Keys) { double percent = colStates[key] / TotalCount; if (percent < 0.05) { OtherCount += colStates[key]; keysToNuke.Add(key); } } foreach (string key in keysToNuke) { colStates[key] = 0; } 

foreach循环中调用ToList() 。 这样我们不需要临时variables副本。 这取决于从.net 3.5开始可用的Linq。

 using System.Linq; foreach(string key in colStates.Keys.ToList()) { double Percent = colStates[key] / TotalCount; if (Percent < 0.05) { OtherCount += colStates[key]; colStates[key] = 0; } } 

您正在修改这一行中的集合:

colStates [key] = 0;

这样做,你基本上删除并重新插入的东西(就IEnumerable而言,无论如何。

如果你编辑你正在存储的值的成员 ,那可以,但是你正在编辑这个值本身,IEnumberable不喜欢这个。

我使用的解决scheme是消除foreach循环,只是使用for循环。 一个简单的循环将不会检查你知道不会影响收集的变化。

以下是你如何做到这一点:

 List<string> keys = new List<string>(colStates.Keys); for(int i = 0; i < keys.Count; i++) { string key = keys[i]; double Percent = colStates[key] / TotalCount; if (Percent < 0.05) { OtherCount += colStates[key]; colStates[key] = 0; } } 

您无法直接在ForEach中修改密钥或值,但可以修改其成员。 例如,这应该工作:

 public class State { public int Value; } ... Dictionary<string, State> colStates = new Dictionary<string,State>(); int OtherCount = 0; foreach(string key in colStates.Keys) { double Percent = colStates[key].Value / TotalCount; if (Percent < 0.05) { OtherCount += colStates[key].Value; colStates[key].Value = 0; } } colStates.Add("Other", new State { Value = OtherCount } ); 

如何做一些你的字典linq查询,然后绑定你的图表的结果呢?…

 var under = colStates.Where(c => (decimal)c.Value / (decimal)totalCount < .05M); var over = colStates.Where(c => (decimal)c.Value / (decimal)totalCount >= .05M); var newColStates = over.Union(new Dictionary<string, int>() { { "Other", under.Sum(c => c.Value) } }); foreach (var item in newColStates) { Console.WriteLine("{0}:{1}", item.Key, item.Value); } 

如果你有创意,你可以做这样的事情。 通过词典向后循环进行更改。

 Dictionary<string, int> collection = new Dictionary<string, int>(); collection.Add("value1", 9); collection.Add("value2", 7); collection.Add("value3", 5); collection.Add("value4", 3); collection.Add("value5", 1); for (int i = collection.Keys.Count; i-- > 0; ) { if (collection.Values.ElementAt(i) < 5) { collection.Remove(collection.Keys.ElementAt(i)); ; } } 

当然不完全相同,但你可能会感兴趣

您需要从旧的创build一个新的词典,而不是修改。 类似于(也遍历KeyValuePair <,>而不是使用密钥查找:

 int otherCount = 0; int totalCounts = colStates.Values.Sum(); var newDict = new Dictionary<string,int>(); foreach (var kv in colStates) { if (kv.Value/(double)totalCounts < 0.05) { otherCount += kv.Value; } else { newDict.Add(kv.Key, kv.Value); } } if (otherCount > 0) { newDict.Add("Other", otherCount); } colStates = newDict; 

你不能修改集合,甚至不能修改值。 您可以保存这些案件,并稍后将其删除。 它会像这样结束:

  Dictionary<string, int> colStates = new Dictionary<string, int>(); // ... // Some code to populate colStates dictionary // ... int OtherCount = 0; List<string> notRelevantKeys = new List<string>(); foreach (string key in colStates.Keys) { double Percent = colStates[key] / colStates.Count; if (Percent < 0.05) { OtherCount += colStates[key]; notRelevantKeys.Add(key); } } foreach (string key in notRelevantKeys) { colStates[key] = 0; } colStates.Add("Other", OtherCount); 

免责声明:我没有做太多的C#

您正在尝试修改存储在HashTable中的DictionaryEntry对象。 Hashtable只存储一个对象 – DictionaryEntry的实例。 改变键或值就足以改变HashTable并导致枚举器失效。

你可以在循环之外做到这一点:

 if(hashtable.Contains(key)) { hashtable[key] = value; } 

首先创build一个你想改变的值的所有键的列表,而不是遍历该列表。

您可以制作dict.Values的列表副本,然后您可以使用List.ForEach lambda函数进行迭代(或之前build议的foreach循环)。

 new List<string>(myDict.Values).ForEach(str => { //Use str in any other way you need here. Console.WriteLine(str); }); 

从.NET 4.5开始您可以使用ConcurrentDictionary来完成此操作:

 using System.Collections.Concurrent; var colStates = new ConcurrentDictionary<string,int>(); colStates["foo"] = 1; colStates["bar"] = 2; colStates["baz"] = 3; int OtherCount = 0; int TotalCount = 100; foreach(string key in colStates.Keys) { double Percent = (double)colStates[key] / TotalCount; if (Percent < 0.05) { OtherCount += colStates[key]; colStates[key] = 0; } } colStates.TryAdd("Other", OtherCount); 

但是请注意,它的性能实际上是一个简单的foreach dictionary.Kes.ToArray()

 using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; public class ConcurrentVsRegularDictionary { private readonly Random _rand; private const int Count = 1_000; public ConcurrentVsRegularDictionary() { _rand = new Random(); } [Benchmark] public void ConcurrentDictionary() { var dict = new ConcurrentDictionary<int, int>(); Populate(dict); foreach (var key in dict.Keys) { dict[key] = _rand.Next(); } } [Benchmark] public void Dictionary() { var dict = new Dictionary<int, int>(); Populate(dict); foreach (var key in dict.Keys.ToArray()) { dict[key] = _rand.Next(); } } private void Populate(IDictionary<int, int> dictionary) { for (int i = 0; i < Count; i++) { dictionary[i] = 0; } } } public class Program { public static void Main(string[] args) { BenchmarkRunner.Run<ConcurrentVsRegularDictionary>(); } } 

结果:

  Method | Mean | Error | StdDev | --------------------- |----------:|----------:|----------:| ConcurrentDictionary | 182.24 us | 3.1507 us | 2.7930 us | Dictionary | 47.01 us | 0.4824 us | 0.4512 us |