web-dev-qa-db-fra.com

La collection C # a été modifiée; l'opération d'énumération peut ne pas s'exécuter

Duplicata possible:
La collection a été modifiée; l'opération d'énumération peut ne pas s'exécuter

Salut,

Je crée un programme d'estimation de projet et j'obtiens l'erreur suivante: la collection C # a été modifiée; L'opération d'énumération peut ne pas s'exécuter.

Cela est lié à l'utilisation de ceci: je déclare initialement le dictionnaire globalement avec ceci:

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

La méthode suivante contenant ce dictionnaire effectue les opérations suivantes:

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

Je l'appelle pour la deuxième fois ici:

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

Le problème se pose dans cette zone du code:

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

Toute aide sur l'endroit où se situe le problème sera très appréciée

32
GreeneScreen

Toute collection que vous parcourez avec foreach ne peut pas être modifiée pendant l'itération.

Ainsi, pendant que vous exécutez un foreach sur les classements, vous ne pouvez pas modifier ses éléments, en ajouter de nouveaux ou en supprimer.

90
Roy Dictus

L'erreur vous indique exactement quel est le problème (et l'exécution dans le débogueur ou la lecture de la trace de la pile vous dira exactement où est le problème):

La collection C # a été modifiée; L'opération d'énumération peut ne pas s'exécuter.

Votre problème est la boucle

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

dans lequel vous modifiez la collection rankings. En particulier, la ligne offensive est

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

Avant d'entrer dans la boucle, ajoutez la ligne suivante:

var listOfRankingsToModify = new List<int>();

Remplacer la ligne incriminée par

listOfRankingsToModify.Add(kvp.Key);

et après avoir quitté la boucle

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

Autrement dit, enregistrez les modifications que vous devez apporter et effectuez-les sans itérer sur la collection que vous devez modifier.

18
jason

Comme d'autres l'ont souligné, vous modifiez une collection sur laquelle vous effectuez une itération et c'est ce qui cause l'erreur. Le code incriminé est ci-dessous:

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

Ce qui peut ne pas être évident dans le code ci-dessus, c'est d'où vient le Enumerator. Dans un article de blog d'un il y a quelques années Eric Lippert fournit un exemple de ce à quoi une boucle foreach est étendue par le compilateur. Le code généré ressemblera à quelque chose comme:

{
    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();
    }
}

Si vous recherchez la documentation MSDN pour IEnumerable (qui est ce que GetEnumerator() renvoie), vous verrez:

Les énumérateurs peuvent être utilisés pour lire les données de la collection, mais ils ne peuvent pas être utilisés pour modifier la collection sous-jacente.

Ce qui nous ramène à ce que le message d'erreur indique et aux autres réponses, vous modifiez la collection sous-jacente.

13
R0MANARMY

Je soupçonne que l'erreur est causée par ceci:

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

le classement est un dictionnaire, qui est IEnumerable. En l'utilisant dans une boucle foreach, vous spécifiez que vous souhaitez que chaque KeyValuePair du dictionnaire soit différée. Autrement dit, le prochain KeyValuePair n'est pas renvoyé jusqu'à ce que votre boucle réitère.

Mais vous modifiez le dictionnaire dans votre boucle:

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

ce qui n'est pas autorisé ... vous obtenez donc l'exception.

Vous pouvez simplement faire ça

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

Le problème est où vous exécutez:

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

Vous ne pouvez pas modifier la collection que vous parcourez dans une boucle foreach. Une boucle foreach nécessite que la boucle soit immuable pendant l'itération.

Utilisez plutôt une boucle "pour" standard ou créez une nouvelle boucle qui est une copie et parcourez-la tout en mettant à jour votre original.

2
Jordan Parmer