コレクションからアイテムを削除するための最良の方法
C#でコレクションからアイテムを削除する場合、アイテムはわかっているがインデックスがわからない場合、どのような方法があるのでしょうか。 これは、それを行うための1つの方法ですが、それは最高のエレガントでないように思われます。
//Remove the existing role assignment for the user.
int cnt = 0;
int assToDelete = 0;
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name == shortName)
{
assToDelete = cnt;
}
cnt++;
}
workspace.RoleAssignments.Remove(assToDelete);
私が本当にやりたいことは、コレクション全体をループして2つの追加変数を使用することなく、プロパティ(この場合、名前)で削除する項目を見つけることです。
69
14
RoleAssignmentsが
List
の場合、以下のコードを使用することができます。もし、コレクションのメンバーのいずれかのプロパティでアクセスしたい場合は、代わりに
Dictionary
またはKeyedCollection
を使うことを検討するとよいでしょう。こうすれば、探しているアイテムを検索する必要がなくなります'。そうでなければ、せめてこれくらいはしてほしい。
@smaclellは、@ sambo99へのコメントで逆反復がより効率的だった理由を尋ねました。
_時々_それはより効率的です。 人のリストがあり、信用格付けを持つすべての顧客を削除またはフィルタリングしたいとします< 1000;。
以下のデータがあります。
前に繰り返すと、すぐに問題が発生します。
idx = 0で「ボブ」を削除し、残りのすべての要素を残します。 次回はループidx = 1ですが。 list [1]は、「Mary」ではなく「Ted」になりました。 誤って「Mary」をスキップしてしまいます。 whileループを使用して、より多くの変数を導入できます。
または、反復を逆にします。
削除されたアイテムの左側にあるすべてのインデックスは同じままなので、アイテムをスキップしないでください。
配列から削除するインデックスのリストが表示されている場合も、同じ原則が適用されます。 物事をまっすぐに保つには、リストを並べ替えて、アイテムを最高インデックスから最低インデックスに削除する必要があります。
これで、Linqを使用して、実行していることを簡単な方法で宣言できます。
---。
単一のアイテムを削除する場合、前方または後方へのイテレーションはより効率的ではありません。 これにはLinqを使用することもできます。
単純なリストの場合< T>最も効率的な方法は、Predicate RemoveAll実装を使用することです。
例えば。
理由は次のとおりです。
1。 Predicate / Linq RemoveAllメソッドはList< T>に実装されています。実際のデータを格納する内部配列にアクセスできます。 データをシフトし、内部配列のサイズを変更します。 2。 RemoveAtメソッドの実装は非常に遅く、データの基になる配列全体を新しい配列にコピーします。 これは、逆反復がList< T>には役に立たないことを意味します。
これをpre c#3.0時代に実装するのに行き詰まっている場合。 2つのオプションがあります。
-簡単にメンテナンスできるオプション。 一致するすべてのアイテムを新しいリストにコピーし、基になるリストを交換します。
例えば。
または。
-トリッキーなわずかに高速なオプション。一致しない場合は、リスト内のすべてのデータをシフトダウンし、配列のサイズを変更します。
リストから頻繁に削除する場合は、HashTable(.net 1.1)またはDictionary(.net 2.0)またはHashSet( .net 3.5)はこの目的に適しています。
それが「ICollection」の場合、「RemoveAll」メソッドはありません。 これを行う拡張方法は次のとおりです。
に基づく: http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collection-class/。
コレクションはどのようなタイプですか?Listであれば、便利な"RemoveAll"を使用することができます。
(これは、.NET 2.0で動作します。もちろん、新しいコンパイラを持っていない場合は、素敵なラムダ構文の代わりに "delegate (SPRoleAssignment spa) { return spa.Member.Name == shortName; }" を使用する必要があります).
Listではなく、ICollectionである場合の別のアプローチです。
これには、Enumerable拡張メソッドが必要です。(.NET 2.0から抜け出せない場合は、Monoのものをコピーすることができます)。もしそれが、アイテムを取ることはできないが、インデックスを取らなければならないカスタムコレクションであれば、Selectなどの他のEnumerableメソッドが、あなたのために整数インデックスを渡してくれます。
これはかなり良い方法です。
http://support.microsoft.com/kb/555972。
これが私の一般的なソリューションです。
拡張方法が参照による通過をサポートする場合、それははるかに単純に見えます。 ! 使用法:
編集:インデックス(idx)を減額してアイテムを削除した場合でも、リストループが有効であることを確認しました。
これを行う最良の方法は、linqを使用することです。
例:
Linqクエリ:
このクエリは、「名前」が「collection2」の要素「名前」と一致する場合、「collection1」からすべての要素を削除します。
必ず使用してください:
System.Li nq;
を使用します。コレクションをループしながらこれを行うため、コレクションの例外の変更を取得しないために、これは私が過去に取ったアプローチです(元のコレクションの最後にある.ToList()に注意してください。これにより、メモリに別のコレクションが作成されます。 、その後、既存のコレクションを変更できます)。
コレクションの使用方法に応じて、別のアプローチをとることができます。 課題を1回ダウンロードする場合(例:.、アプリが実行されると)、その場でコレクションをハッシュテーブルに変換できます。
ショートネーム=> SPROleAssignment。
これを行う場合、短い名前でアイテムを削除する場合、ハッシュテーブルキーからアイテムを削除するだけです。
残念ながら、これらのSPRoleAssignmentsをたくさんロードしている場合、それは明らかに時間の点でこれ以上コスト効率が高くなることはありません。 Linqの使用について他の人が行った提案は、.NET Frameworkの新しいバージョンを使用している場合は良いでしょうが、それ以外の場合は、使用している方法に固執する必要があります。
辞書コレクションの視点と同様に、私はこれをしました。
注意:。 上記のコードは.Net Client Profile(3.5および4.5)で失敗します。また、一部の視聴者はそうであると述べました。 .Net4.0で失敗した場合も、どの設定が問題を引き起こしているのかわかりません。
そのため、そのエラーを回避するために、Whereステートメントの下のコード(.ToList())に置き換えます。 「収集が変更されました。列挙操作を実行できない場合があります。」。
MSDNごと.Net4.5以降、クライアントプロファイルは廃止されます。 http://msdn.microsoft.com/en-us/library/cc656912(v = vs.110).aspx。
アイテムを削除するよりも、最初にアイテムを保存します。
アイテムクラスの「GetHashCode()」をオーバーライドする必要があります。
ここで多くの良い反応があります。ラムダの表現が特に好きです。.。とてもきれい。 しかし、コレクションのタイプを指定しなかったので、私は見逃されました。 これは、(MOSSからの)SPRoleAssignmentCollectionで、Remove(int)とRemove(SPPrincipal)のみがあり、便利なRemoveAll()ではありません。 だから、もっと良い提案がない限り、私はこれに落ち着きました。