C#で繰り返し処理しながら列挙可能なコレクションからアイテムを変更または削除する方法

StackOverflow https://stackoverflow.com/questions/308466

  •  08-07-2019
  •  | 
  •  

質問

データテーブルからいくつかの行を削除する必要があります。コレクションを繰り返し処理している間にコレクションを変更しても大丈夫ではないと聞いたことがあります。したがって、行が削除の要求を満たしているかどうかを確認してから削除済みとしてマークするforループの代わりに、最初にデータテーブルを反復処理し、リスト内のすべての行を追加してから、リストとマークを反復処理する必要があります削除する行。この理由は何ですか?また、私が意味する行リストを使用する代わりに、どのような選択肢がありますか?。

役に立ちましたか?

解決

単純な for ループを使用すると、コレクションから要素を削除できます。

この例を見てください:

        var l = new List<int>();

        l.Add(0);
        l.Add(1);
        l.Add(2);
        l.Add(3);
        l.Add(4);
        l.Add(5);
        l.Add(6);

        for (int i = 0; i < l.Count; i++)
        {
            if (l[i] % 2 == 0)
            {
                l.RemoveAt(i);
                i--;
            }
        }

        foreach (var i in l)
        {
            Console.WriteLine(i);
        }

他のヒント

リストを逆方向に反復することは、より良いアプローチのように思えます。なぜなら、要素と他の要素を「ギャップに陥る」と削除した場合、それらをすでに見ているので問題ではないからです。また、カウンタ変数が.Countより大きくなることを心配する必要はありません。

        List<int> test = new List<int>();
        test.Add(1);
        test.Add(2);
        test.Add(3);
        test.Add(4);
        test.Add(5);
        test.Add(6);
        test.Add(7);
        test.Add(8);
        for (int i = test.Count-1; i > -1; i--)
        {
            if(someCondition){
                test.RemoveAt(i);
            }
        }

@brunoコードを取得して、逆方向に実行します。

後方に移動すると、欠落している配列インデックスがループの順序に干渉しません。

var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });

for (int i = l.Count - 1; i >= 0; i--)
    if (l[i] % 2 == 0)
        l.RemoveAt(i);

foreach (var i in l)
{
    Console.WriteLine(i);
}

しかし、最近では、LINQを使用します:

var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });

l.RemoveAll(n => n % 2 == 0);

DataTableを使用しており、テーブルアダプターを使用して変更をサーバーに永続化できるようにする必要があるため(コメントを参照)、行を削除する方法の例を次に示します。

DataTable dt;
// remove all rows where the last name starts with "B"
foreach (DataRow row in dt.Rows)
{
    if (row["LASTNAME"].ToString().StartsWith("B"))
    {
        // mark the row for deletion:
        row.Delete();
    }
}

行でdeleteを呼び出すと、RowStateプロパティがDeletedに変更されますが、削除された行はテーブルに残ります。変更をサーバーに戻す前にこのテーブルで作業する必要がある場合(テーブルの内容から削除された行を差し引いて表示する場合など)、このように繰り返し処理するときに各行のRowStateを確認する必要があります:

foreach (DataRow row in dt.Rows)
{
    if (row.RowState != DataRowState.Deleted)
    {
        // this row has not been deleted - go ahead and show it
    }
}

コレクションから行を削除すると(ブルーノの答えのように)テーブルアダプターが破損するため、通常はDataTableを使用しないでください。

whileループがこれを処理します:

int i = 0;
while(i < list.Count)
{
    if(<codition for removing element met>)
    {
        list.RemoveAt(i);
    }
    else
    {
        i++;
    }
}

chakritのソリューションは、ラムダ式ではなくデリゲートを使用して.NET 2.0(LINQ /ラムダ式なし)をターゲットにしている場合にも使用できます。

public bool IsMatch(int item) {
    return (item % 3 == 1); // put whatever condition you want here
}
public void RemoveMatching() {
    List<int> x = new List<int>();
    x.RemoveAll(new Predicate<int>(IsMatch));
}

リストの削除中または追加中にリストを繰り返し処理すると、先ほど言ったようにリストが壊れる可能性があります。

この問題を解決するために2つのリストのアプローチをよく使用しました:

ArrayList matches = new ArrayList();   //second list

for MyObject obj in my_list
{

    if (obj.property == value_i_care_about)
        matches.addLast(obj);
}

//now modify

for MyObject m in matches
{
    my_list.remove(m); //use second list to delete from first list
}

//finished.

列挙しているコレクションからアイテムを削除する必要がある場合、通常は逆順に列挙します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top