Pouvez-vous supprimer un élément d’une liste en le parcourant en C #

Pouvez-vous supprimer un élément d’une liste en le parcourant? Cela fonctionnera-t-il ou y a-t-il une meilleure façon de le faire?

Mon code:

foreach (var bullet in bullets) { if (bullet.Offscreen()) { bullets.Remove(bullet); } } 

-edit- Désolé les gars, c’est pour un jeu Silverlight. Je n’avais pas réalisé que silverlight était différent du Compact Framework.

Edit : pour clarifier, la question concerne Silverlight , qui ne prend apparemment pas en charge RemoveAll on List`T . Il est disponible dans le cadre complet, CF, versions XNA 2.0+.

Vous pouvez écrire un lambda qui exprime vos critères de suppression:

 bullets.RemoveAll(bullet => bullet.Offscreen()); 

Ou vous pouvez sélectionner ceux que vous voulez au lieu de supprimer ceux que vous ne voulez pas:

 bullets = bullets.Where(b => !b.OffScreen()).ToList(); 

Ou utilisez l’indexeur pour revenir en arrière dans la séquence:

 for(int i=bullets.Count-1;i>=0;i--) { if(bullets[i].OffScreen()) { bullets.RemoveAt(i); } } 
 bullets.RemoveAll(bullet => bullet.Offscreen()); 

Modifier: pour que cela fonctionne tel quel dans Silverlight, ajoutez la méthode d’extension suivante à votre projet.

Comme pour la List.RemoveAll , cet algorithme est O (N) où N est la longueur de la liste par opposition à O (N * M) où M est le nombre d’éléments supprimés de la liste. Puisqu’il s’agit d’une méthode d’extension avec le même prototype que la méthode RemoveAll trouvée dans les frameworks autres que Silverlight, celle intégrée sera utilisée lorsqu’elle sera disponible, et celle-ci sera utilisée de manière transparente pour les générations silverlight.

 public static class ListExtensions { public static int RemoveAll(this List list, Predicate match) { if (list == null) throw new NullReferenceException(); if (match == null) throw new ArgumentNullException("match"); int i = 0; int j = 0; for (i = 0; i < list.Count; i++) { if (!match(list[i])) { if (i != j) list[j] = list[i]; j++; } } int removed = i - j; if (removed > 0) list.RemoveRange(list.Count - removed, removed); return removed; } } 

Tenter de le supprimer dans une boucle foreach lève une exception. Vous devez parcourir à l’envers avec une boucle for.

 for (int count = bullets.Count - 1; count >= 0; count--) { if (bullets[count].Offscreen()) { //bullets.Remove(bullets[count]); bullets.RemoveAt(count); } } 

Essaye ça:

 bullets.RemoveAll(bullet => bullet.Offscreen()); 

Il est préférable de créer une liste contenant les éléments à supprimer, puis de supprimer des éléments de la liste:

 List removedBullets = new List(); foreach(var bullet in bullets) { if (bullet.OffScreen()) { removedBullets.Add(bullet); } } foreach(var bullet in removedBullets) { bullets.Remove(bullet); } 

Itérez dans la boucle “pour” plutôt que d’itérer dans foreach. Cela fonctionnera.

J’ai déjà rencontré ce problème et blogué à ce sujet ici .

La version courte est que vous pouvez créer une méthode d’extension appelée RemoveIf:

 public void RemoveIf(ICollection collection, Predicate match) { List removed = new List(); foreach (T item in collection) { if (match(item)) { removed.Add(item); } } foreach (T item in removed) { collection.Remove(item); } removed.Clear(); } 

Ensuite, appelez-le avec votre délégué chaque fois que vous en avez besoin:

 RemoveIf(_Entities.Item, delegate(Item i) { return i.OffScreen(); });