我正试图从字典中建立一个饼图。在显示饼图之前,我想整理一下数据。我去掉了所有小于5%的派片,把它们放到“其他”派片里。然而,我得到一个集合被修改;枚举操作在运行时不能执行异常。

我理解为什么在遍历字典时不能从字典中添加或删除项。但是,我不明白为什么不能简单地在foreach循环中更改现有键的值。

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

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;
    }
}

还是……

创建修改列表

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 lambda函数用于迭代(或如前所述的ForEach循环)。

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

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

我明白你的观点,但同时,如果值集合可以在迭代过程中改变,那就太奇怪了——而且为了简单起见,只有一个版本号。

修复这类问题的正常方法是预先复制键的集合并遍历副本,或者遍历原始集合,但保留在完成迭代后应用的更改集合。

例如:

先复制键

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;
    }
}

还是……

创建修改列表

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;
}

你在这一行修改集合:

col各州[键]= 0;

通过这样做,您实际上是在此时删除和重新插入一些内容(就IEnumerable而言)。

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

我使用的解决方案是消除foreach循环,只使用for循环。 简单的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;    
    }
}

在。net 5中,可以在枚举字典时更改字典项。

拉请求是:允许在枚举期间覆盖字典,问题是考虑从字典<TKey, TValue>的覆盖中删除_version++。

现在你可以:

foreach (var pair in dict)
    dict[pair.Key] = pair.Value + 1;

在foreach循环中调用ToList()。这样我们就不需要一个临时变量的拷贝。它依赖于自。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;
    }
}