在foreach循环中编辑字典值

Ahe*_*eho 180 .net c# .net-2.0

我正在尝试从字典中构建饼图.在我显示饼图之前,我想整理数据.我正在删除任何小于饼的5%的饼图,并将它们放入"其他"饼图中.但是我Collection was modified; enumeration operation may not execute在运行时遇到异常.

我理解为什么你不能在迭代它们时添加或删除字典中的项目.但是我不明白为什么你不能简单地改变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);
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 248

在字典中设置值会更新其内部"版本号" - 使迭代器无效,以及与键或值集合关联的任何迭代器.

我确实看到了你的观点,但与此同时,如果值集合可能在迭代中发生变化,那将会很奇怪 - 为简单起见,只有一个版本号.

修复此类事情的常规方法是先预先复制密钥集合并迭代副本,或者迭代原始集合,但保留一系列更改,您将在完成迭代后应用这些更改.

例如:

首先复制密钥

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

要么...

创建修改列表

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我知道这是旧的,但是如果使用.NET 3.5(或者它是4.0?),您可以使用和滥用LINQ,如下所示:foreach(colStates.Keys.ToList()中的字符串键){...} (20认同)
  • @Machtyn:当然 - 但问题是关于.NET 2.0,否则我肯定*会使用LINQ. (5认同)
  • 显然,.net5 允许在迭代时使用 setter 更新字典值 (2认同)

小智 68

ToList()foreach循环中调用.这样我们就不需要临时变量副本了.这取决于自.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 最好使用 `foreach(var pair in colStates.ToList())` 来避免访问 Key * 和 * 值,从而避免调用 `colStates[key]`.. (4认同)

Cod*_*ile 20

您正在修改此行中的集合:

colStates [key] = 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;    
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 此代码中的修复不是 for 循环:它正在复制键列表。(如果您将其转换为 foreach 循环,它仍然可以工作。)使用 for 循环解决将意味着使用 `colStates.Keys` 代替 `keys`。 (3认同)

tym*_*tam 7

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

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

现在你可以:

foreach (var pair in dict)
    dict[pair.Key] = pair.Value + 1;
Run Code Online (Sandbox Code Playgroud)


Jer*_*rey 6

您不能直接在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 } );
Run Code Online (Sandbox Code Playgroud)