web-dev-qa-db-ja.com

C#コレクションが変更されました。列挙操作が実行されない可能性があります

可能性のある複製:
コレクションが変更されました。列挙操作は実行されない可能性があります

こんにちは、

プロジェクト見積もりプログラムを作成していますが、次のエラーが表示されます。C#コレクションが変更されました。列挙操作が実行されない場合があります。

これはこれを使用することに関連しています:私は最初にこれで辞書に載っている辞書を宣言します:

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

このディクショナリを含む次のメソッドは次を実行します。

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

問題がどこにあるかについての助けは大歓迎です

32
GreeneScreen

Foreachで反復処理するコレクションは、反復処理中に変更することはできません。

したがって、ランキングに対してforeachを実行している間は、その要素を変更したり、新しい要素を追加したり、要素を削除したりすることはできません。

90
Roy Dictus

このエラーは、問題の内容を正確に示します(デバッガーで実行するか、スタックトレースを読み取ると、問題の正確な場所がわかります)。

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

つまり、必要な変更を記録し、変更する必要のあるコレクションを繰り返し処理することなく変更を加えます。

18
jason

他の人が指摘しているように、繰り返し処理しているコレクションを変更しているので、それがエラーの原因です。問題のコードは以下のとおりです。

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

IEnumerableGetEnumerator()が返すもの)のMSDNドキュメントを参照すると、次のように表示されます。

列挙子はコレクション内のデータの読み取りに使用できますが、基になるコレクションの変更には使用できません。

これにより、エラーメッセージの状態と他の回答の状態に戻り、基礎となるコレクションを変更しています。

13
R0MANARMY

エラーの原因は次のとおりです。

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

ランキングは辞書であり、IEnumerableです。 foreachループで使用することにより、ディクショナリの各KeyValuePairを遅延させたいことを指定しています。つまり、ループが再度繰り返されるまで、次のKeyValuePairは返されません。

しかし、ループ内で辞書を変更しています:

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

許可されていません...そのため、例外が発生します。

あなたは単にこれを行うことができます

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

問題はあなたが実行している場所です:

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

Foreachループで繰り返し処理しているコレクションを変更することはできません。 foreachループでは、反復中にループが不変である必要があります。

代わりに、標準の「for」ループを使用するか、コピーである新しいループを作成して、元のファイルを更新しながら繰り返します。

2
Jordan Parmer