ループでIenumerableコレクションからオブジェクトを削除する方法[複製]
-
03-07-2019 - |
質問
この質問にはすでに回答があります:
複製
列挙可能なコレクション(辞書のIListやKeyValuePairsなど)をループしている間、オブジェクトを削除できない問題を回避できる素敵なパターンが誰にもあります
たとえば、foreachの間に列挙されたリストを変更するため、以下は失敗します
foreach (MyObject myObject in MyListOfMyObjects)
{
if (condition) MyListOfMyObjects.Remove(myObject);
}
過去に2つの方法を使用しました。
foreachを逆forループに置き換えました(オブジェクトを削除してもループしているインデックスが変更されないようにするためです)。
オブジェクトの新しいコレクションを保存してループ内で削除し、そのコレクションをループして元のコレクションからオブジェクトを削除しようとしました。
これらは正常に動作しますが、どちらも気分が良くありません。問題に対するエレガントな解決策を誰かが思いついたのではないかと思いました
解決
このために設計されていると思う便利なList<T>.RemoveAll(Predicate<T> match)
メソッドがあります: http ://msdn.microsoft.com/en-us/library/wdka673a.aspx
他のヒント
これは一種の単純な考え方ですが、IEnumerable / IListからアイテムを削除する場合、通常はコピーを作成します。
foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
if (condition) MyListOfMyObjects.Remove(myObject);
}
これは最も効率的な方法ではありませんが、読みやすいです。早すぎる最適化とそのすべて。
逆を行い、新しいリストを作成します:
List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
if (!condition) myFilteredList.Add(myObject);
}
新しいリストを必要な場所で使用します。
条件を逆にして、LINQ式を簡単に使用することもできます。これには、新しい構造を作成しないという追加の利点がありますが、怠zyな列挙型であるという落とし穴もあります。
var myFilteredList = from myObject in myListOfMyObjects
where !condition
select myObject;
ただし、リストからアイテムを本当に削除する必要がある場合、通常は<!> quot;新しいリストを作成してから、繰り返して削除します<!> quot;アプローチ。
この投稿に出会い、共有したいと思いました。
void RemoveAll(object condition)
{
bool found = false;
foreach(object thisObject in objects)
{
if (condition)
{
objects.Remove(thisObject);
found = true;
break; //exit loop
}
}
// Call again recursively
if (found) RemoveAll(condition);
}
特定のデータ構造でのみ機能するため、逆forループのアイデアは好きではありません。
通常、2番目の手法を使用して、削除するアイテムを別の「削除対象」コレクションに蓄積します。削除によって既存の反復が無効になる可能性がある場合(たとえば、バランスのとれたツリーコレクションで発生する場合)、これを回避する方法はありません。
私がときどき使用した他のテクニックは、削除する最初の要素が見つかったときに反復全体を再起動することです。削除するアイテムが見つからない場合は、機能が終了します。これは非効率的ですが、コレクションから1つのアイテムを削除すると、削除する必要のあるアイテムのセットが変更される可能性がある場合に必要になることがあります。
辞書があり、すべての値を破棄したい。各値が破棄されると、辞書から自身が削除され、議論する問題が発生します。私は次のことをしました:
foreach (var o in dictionary.Values.ToList())
{
o.Dispose();
}
これは今は死んでいるかもしれないことを感謝していますが、私はいつもこれを行う方法は次のとおりです:
foreach(MyListOfMyObjectsのMyObject myObject)
{
if(条件)MyListOfMyObjects.Remove(myObject);
break;
}
オブジェクトが削除され、ループが終了します、ビオラ!