列挙者の問題、2つのループを回避する方法はありますか?
-
28-09-2019 - |
質問
サードパーティのAPIがあり、クラス内のさまざまなアイテムの列挙器を返すクラスがあります。
その列挙者のアイテムを削除する必要があるので、「それぞれに」を使用できません。私が考えることができるオプションのみが、列挙上を反復することでカウントを取得し、ループの通常を実行してアイテムを削除することです。
2つのループを避ける方法を知っている人はいますか?
ありがとう
アップデート 混乱して申し訳ありませんが、コメントの下のアンドレイは正しいです。
ここに、私の頭からのいくつかの擬似コードが機能しません。
for each (myProperty in MyProperty)
{
if (checking some criteria here)
MyProperty.Remove(myProperty)
}
MyPropertyは、列挙器と削除方法を実装するサードパーティクラスです。
解決
一般的なパターンは、このようなことをすることです。
List<Item> forDeletion = new List<Item>();
foreach (Item i in somelist)
if (condition for deletion) forDeletion.Add(i);
foreach (Item i in forDeletion)
somelist.Remove(i); //or how do you delete items
他のヒント
一度ループして、削除してはならないアイテムを含む2番目の配列を作成します。
それがコレクションであることがわかっている場合は、次のように戻ることができます。
for (int i = items.Count - 1; i >= 0; i--)
{
items.RemoveAt(i);
}
それ以外の場合は、2つのループを実行する必要があります。
このようなものを作成できます。
public IEnumerable<item> GetMyList()
{
foreach (var x in thirdParty )
{
if (x == ignore)
continue;
yield return x;
}
}
その列挙器のアイテムを削除する必要があります
これが問題ではない単一のアイテムである限り。ルールはあなたです 続行できません コレクションを変更した後に反復します。したがって:
foreach (var item in collection) {
if (item.Equals(toRemove) {
collection.Remove(toRemove);
break; // <== stop iterating!!
}
}
列挙器からアイテムを削除することはできません。できることは、列挙シーケンス全体のコンテンツをコピーまたはフィルタリング(またはその両方)することです。 LINQを使用してこれを達成し、次のようにSMTHを行うことができます。
YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
APIと使用しているAPI呼び出しについて詳しく説明できますか?
あなたが受け取った場合 IEnumerator<T>
また IEnumerable<T>
列挙器の背後にあるシーケンスからアイテムを削除することはできません。そして、実装が変更される可能性があるため、受け取ったオブジェクトのキャストに頼ることに頼るべきではありません。 (実際には、適切に設計されたAPIは、内部状態を保持している可変オブジェクトをまったく露出してはなりません。)
受け取ったら IList<T>
または、通常のものを使用できるようなものです for
どの状態が破損する可能性があるため、背面から前後にループして、必要に応じてアイテムを削除します。 (ここで、可変状態の公開に関する規則が再度適用されるはずです - 返されたコレクションを変更することは状態を変更してはなりません。)
ienumerator.count()は、実行時に何をする必要があるかを決定します - それがコレクションであることを確認するためにカウントまたは反映することを列挙し、そのように電話をかけます。
私はSjoerdの提案が好きですが、私たちが話しているアイテムの数について心配しています。
なぜ。
// you don't want 2 and 3
IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
これを行うためのきれいで読みやすい方法は次のとおりです(私はあなたがそれを指定していないので、ここでサードパーティのコンテナのAPIを推測しています)。
foreach(var delItem in ThirdPartyContainer.Items
.Where(item=>ShouldIDeleteThis(item))
//or: .Where(ShouldIDeleteThis)
.ToArray()) {
ThirdPartyContainer.Remove(delItem);
}
通話 .ToArray()
削除されるすべてのアイテムが、foreach反復が始まる前に貪欲にキャッシュされていることを保証します。
舞台裏では、これにはアレイとその余分な反復が含まれますが、それは一般的に非常に安価であり、この質問に対する他の回答よりもこの方法の利点は、単純な列挙に機能し、トリッキーな可変状態の問題を伴わないことです。読みにくく、誤解しやすい。
対照的に、ロケット科学ではありませんが、逆に繰り返しますが、オフ1つのエラーが発生しやすく、読みにくいです。また、削除の間に順序を変更しないなど、コレクションの内部にも依存しています(たとえば、バイナリヒープではない場合、たとえば)。一時的なリストに削除する必要があるアイテムを手動で追加することは、不必要なコードです。それが.toarray()が正常に行うことです:-)。
列挙者は、常に実際のコレクションを指すプライベートフィールドを持っています。
Reflection.Modifyを介して入手できます。
楽しんで。