C#集合已被修改;枚举操作可能无法执行。

41

可能是重复问题:
Collection was modified; enumeration operation may not execute

你好,

我正在创建一个项目预估程序,并遇到以下错误:C# Collection was modified; enumeration operation may not execute.

这与使用以下内容有关: 我最初使用以下内容在全局范围内声明字典:

Dictionary<int, int> rankings = new Dictionary<int, int>();

包含这个字典的 Next 方法执行以下操作:

private void getFirstEstimation()
{
    List<int> array = new List<int>();

    string strConnection = ConfigurationSettings.AppSettings["ConnectionString"];
    MySqlConnection connection = new MySqlConnection(strConnection);
    MySqlCommand command = connection.CreateCommand();
    MySqlDataReader reader;
    command.CommandText = "SELECT idprojects FROM `test`.`projects` WHERE application_layers = " + applicationTiers;
    connection.Open();

    reader = command.ExecuteReader();
    while (reader.Read())
    {
        array.Add(Convert.ToInt32(reader["idprojects"].ToString()));
    }
    foreach (int i in array)
    {
        rankings[i] = 15;
    }
    connection.Close();
}

我在这里第二次调用它:

private void getSecondEstimation()
{
    Dictionary<int, string> sqltext = new Dictionary<int, string>();
    Dictionary<int, int> valueForSql = new Dictionary<int, int>();
    Dictionary<int, int> weightings = new Dictionary<int, int>();
    sqltext.Add(1, "project_type");
    valueForSql.Add(1, projectType);
    weightings.Add(1, 10);
    sqltext.Add(2, "application_domain");
    valueForSql.Add(2, applicationDomain);
    weightings.Add(2, 8);
    sqltext.Add(3, "organisation_size");
    valueForSql.Add(3, organizationSize);
    weightings.Add(3, 8);
    sqltext.Add(4, "no_of_locations");
    valueForSql.Add(4, noOfLocations);
    weightings.Add(4, 7);
    sqltext.Add(5, "development_process");
    valueForSql.Add(5, developmentProcess);
    weightings.Add(5, 6);
    sqltext.Add(6, "rules_engine");
    valueForSql.Add(6, rulesEngine);
    weightings.Add(6, 5);
    sqltext.Add(7, "middleware");
    valueForSql.Add(7, middleware);
    weightings.Add(7, 4);
    sqltext.Add(8, "location_of_development");
    valueForSql.Add(8, locationOfDevelopment);
    weightings.Add(8, 3);
    sqltext.Add(9, "programming_language");
    valueForSql.Add(9, programmingLanguage);
    weightings.Add(9, 3);
    sqltext.Add(10, "development_environment");
    valueForSql.Add(10, developmentEnvironment);
    weightings.Add(10, 3);
    sqltext.Add(11, "backend");
    valueForSql.Add(11, backend);
    weightings.Add(11, 3);
    sqltext.Add(12, "webserver");
    valueForSql.Add(12, webServer);
    weightings.Add(12, 3);

    List<int> array = new List<int>();

    string strConnection = ConfigurationSettings.AppSettings["ConnectionString"];
    MySqlConnection connection = new MySqlConnection(strConnection);
    MySqlCommand command = connection.CreateCommand();
    MySqlDataReader reader;

    for (int i = 1; i <= 12; i++)
    {
        command.CommandText = "SELECT idprojects FROM `test`.`projects` WHERE " + sqltext[i] + " = " + valueForSql[i];
        connection.Open();
        //int testInt;
        reader = command.ExecuteReader();
        while (reader.Read())
        {
            array.Add(Convert.ToInt32(reader["idprojects"].ToString()));
        }
        foreach (int a in array)
        {
            if (!rankings.ContainsKey(a))
            {
                rankings[a] = 0;
            }
            rankings[a] = rankings[a] + weightings[i];
        }
        connection.Close();
    }       
}
问题出现在代码的这个区域:
private void getThirdEstimation()
{
    ArrayList tempModuleHolder;

    string strConnection = ConfigurationSettings.AppSettings["ConnectionString"];
    MySqlConnection connection = new MySqlConnection(strConnection);
    MySqlCommand command = connection.CreateCommand();
    MySqlDataReader reader;
    int similarModules;

    foreach (KeyValuePair<int, int> kvp in rankings)
    {
        similarModules = 0;
        tempModuleHolder = new ArrayList();
        command.CommandText = "SELECT id_modules FROM `test`.`modules_in_project` WHERE id_project = " + kvp.Key;
        connection.Open();

        reader = command.ExecuteReader();
        while (reader.Read())
        {
            tempModuleHolder.Add(Convert.ToInt32(reader["id_modules"].ToString()));
        }

        foreach (int i in tempModuleHolder)
        {
            if(modules.Contains(i))
            {
                similarModules++;
            }
        }
        if((double)(similarModules/modules.Count)>0.6)
        {
            //kvp.Value = kvp.Value + 4;
            rankings[kvp.Key] = rankings[kvp.Key] + 4;
        }
        connection.Close();
    }
}
任何对问题所在的帮助都将不胜感激

顺便说一句,跑题了,您的MySqlConnection和MySqlDataReader类未实现IDisposable接口的Dispose()方法,这样您就可以在连接和读取器周围使用using块了吗? - Ashish Gupta
1
@ydobonmai:MySqlConnectionMySqlDataReader可能是用于处理MySQL数据库的MySql.Data.MySqlClient类。因此,它们实现了IDisposable接口,应该在using块中使用。 - jason
5个回答

103

使用foreach迭代的任何集合在迭代过程中都不能进行修改。

因此,在运行foreach循环时,不能修改rankings中的元素、添加新元素或删除任何元素。


34
你可以通过对集合的副本进行循环来解决问题。例如,在循环实例化时使用 Collection.ToArray()。这将使你能够在循环中修改实际的集合,而不会改变集合的“副本”。来源:https://dev59.com/9HRB5IYBdhLWcg3wgHWr - Don Thomas Boyle
5
Don说:ToArray()修复了它。同时也要点赞Roy。 - Mukus
我想知道为什么这个错误还没有修复。 - Filip Vondrášek

24

这个错误告诉你问题所在(使用调试器或阅读堆栈跟踪可以确切地告诉你问题出现在哪里):

C# 集合已经被修改,枚举操作可能无法执行。

你的问题出现在循环结构中。

foreach (KeyValuePair<int, int> kvp in rankings) {
    //
}

在这里,您修改了集合rankings。 特别是进攻线

rankings[kvp.Key] = rankings[kvp.Key] + 4;

在进入循环之前,添加以下行:

var listOfRankingsToModify = new List<int>();

将有问题的那行代码替换为

listOfRankingsToModify.Add(kvp.Key);

当你退出循环后

foreach(var key in listOfRankingsToModify) {
    rankings[key] = rankings[key] + 4;
}

也就是说,记录需要进行的更改,并在不迭代需要修改的集合的情况下进行更改。


14

正如其他人指出的那样,您正在修改一个正在迭代的集合,这就是导致错误的原因。以下是有问题的代码:

foreach (KeyValuePair<int, int> kvp in rankings)
{
    .....

    if((double)(similarModules/modules.Count)>0.6)
    {
        rankings[kvp.Key] = rankings[kvp.Key] + 4;  // <--- This line is the problem
    }
    .....

从上述代码中可能不明显的是 Enumerator 来自哪里。在 Eric Lippert 的博客文章中,他提供了一个 几年前的示例,展示编译器将一个 foreach 循环如何展开。生成的代码类似于:

{
    IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator(); // <-- This
                                                       // is where the Enumerator
                                                       // comes from.
    try
    { 
        int m; // OUTSIDE THE ACTUAL LOOP in C# 4 and before, inside the loop in 5
        while(e.MoveNext())
        {
            // loop code goes here
        }
    }
    finally
    { 
      if (e != null) ((IDisposable)e).Dispose();
    }
}
如果你查看MSDN文档中关于IEnumerable的说明(这是GetEnumerator()返回的内容),你会看到:

Enumerator可用于读取集合中的数据,但不能用于修改底层集合。

这就带我们回到错误消息所述和其他答案所重申的问题,即你正在修改底层集合。


潜在读者:请忽略下投票,这是出于愤怒的情绪。罗曼:如果您编辑此答案,我将取消下投票,因为已经超过时间限制,无法让我这样做。 - heisenberg

9

我怀疑这个错误是由以下原因引起的:

foreach (KeyValuePair<int, int> kvp in rankings)

rankings是一个字典,它是IEnumerable。通过在foreach循环中使用它,您指定希望以延迟方式获取字典中的每个KeyValuePair。也就是说,下一个KeyValuePair直到循环再次迭代才会返回。

但是您正在循环内修改字典:

rankings[kvp.Key] = rankings[kvp.Key] + 4;

这是不允许的...所以你会得到异常。

你可以简单地这样做

foreach (KeyValuePair<int, int> kvp in rankings.ToArray())

3
问题出在您执行代码的位置:
rankings[kvp.Key] = rankings[kvp.Key] + 4;

您不能在foreach循环中修改正在遍历的集合。在迭代时,foreach循环要求循环保持不变。
相反,使用标准的“for”循环或创建一个新的循环进行复制,并在更新原始循环时遍历该循环的副本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接