web-dev-qa-db-ja.com

C#で反復しながら列挙可能なコレクションの項目を変更または削除する方法

データテーブルからいくつかの行を削除する必要があります。反復しながらコレクションを変更することは問題ないと聞いたことがあります。したがって、行が削除の要求を満たしているかどうかを確認してから削除済みとしてマークするforループの代わりに、まずデータテーブルを反復処理してリスト内のすべての行を追加し、次にリストを反復処理してマークを付ける必要があります削除する行。これの理由は何ですか?(つまり、行リストを使用する代わりに)代替手段はありますか?.

38
kjv

単純な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);
        }
18
bruno conde

リストを逆方向に反復することは、より良いアプローチのように聞こえます。なぜなら、要素と他の要素を「ギャップに陥る」ように削除した場合、すでにそれらを見ているので、それは問題ではないからです。また、カウンター変数が.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);
            }
        }
88
Michael Stum

@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);
33
chakrit

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で実行しないでください。

5
MusiGenesis

Whileループはこれを処理します。

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

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));
}
3
Ian Hopkins

繰り返しながらリストを削除または追加すると、あなたが言ったように、リストが壊れる可能性があります。

私はよく問題を解決するために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.
2
Philluminati

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

0
Adrian