2010-12-08 3 views
0

Possible Duplicate:
How to modify or delete items from an enumerable collection while iterating through it in C#Comment puis-je faire ce spécial foreach itérateur?

Écoutez, je ne veux pas connaître le foreach de base. Je parle de celui qui contrôle cette erreur:

 
"The enumerator is not valid because the collection changed." 

Ce qui se produit quand je fais ceci:

foreach(Image image in images) 
{ 
    if(...) 
    { 
     images.remove(image) 
    } 
} 

Je crois qu'il ya un itérateur spécial qui gèrent bien cela, comme Java a. Alors, comment puis-je faire cela en C# s'il vous plaît? Merci!

Répondre

4
for (var i = 0; i < images.Count; ++i) 
{ 
    if (...) 
    { 
     images.RemoveAt(i); 
     --i; 
    } 
} 
+0

Mais si je fais cela, je crois que je vais perdre l'objet suivant, étant donné que j'utilise Canvas. Par exemple: Image que j'ai supprimé l'objet de l'index 2. Donc l'objet de l'index 2 se déplacera à l'index 2 en tant que liste. Donc, quand j'essaie d'obtenir la valeur suivante qui est l'index 3, l'ancien objet de l'index 3 sera à l'index 2. Donc je vais le perdre. – Seva

+0

@Alan, c'est le but de la ligne '--i;'. Ceci ajuste l'index de sorte qu'aucun élément ne soit manqué. –

+0

c'est ce que corrige le '--i'. La technique de décrémentation lors du retrait est intéressante. D'habitude, j'écrivais ma boucle pour revenir en arrière et parfois faire un double test. – CodesInChaos

2

Vous ne pouvez pas le faire en C#.

Ce que vous pouvez faire est de collecter les objets que vous souhaitez supprimer, puis les enlever:

Image[] imagesToRemove = images.Where(image => ...).ToArray(); 
foreach (Image image in imagesToRemove) 
    images.remove(image); 
+0

Vous avez oublié le 'ToArray()' – CodesInChaos

+0

Je l'ai fait, je viens de réaliser :) –

+0

Et je pense que votre code est généralement O (n^2) puisque 'Remove' sur la plupart des collections est O (n). Sur 'HashSet ' ce serait rapide, mais HashSet offre déjà des méthodes intégrées pour cela. – CodesInChaos

5

Ou tout simplement le retirer sans itérer manuellement du tout:

images.RemoveAll(image=>...) 

Travaux sur List<T> mais beaucoup les autres conteneurs ne le supportent pas.

Une solution O (n) travaillant sur IList<T>:

void RemoveWhere(this IList<T> list,Predicate<T> pred) 
{ 
    int targetIndex=0; 
    for(int srcIndex=0;srcIndex<list.Count;srcIndex++) 
    { 
     if(pred(list[srcIndex])) 
     { 
     list[targetIndex]=list[srcIndex]; 
     targetIndex++; 
     } 
     for(int i=list.Count-1;i>=targetIndex;i--) 
     list.RemoveAt(i); 
    } 
} 

peut être accéléré un peu en n'affectant jusqu'à ce que vous frappez le premier élément supprimé.

+0

dans mon cas, je ne retirerai SI une condition est vraie. – Seva

+0

Le lamda est une condition. Le nom des fonctions est trompeur. Je l'aurais appelé 'RemoveWhere' – CodesInChaos

+0

hélas, RemoveAll est l'une des méthodes d'extension LINQ, donc le nommer quelque chose d'autre n'est pas vraiment une option. Je suis d'accord, cependant, que cette méthode, avec d'autres, ont été mal nommé. –

1

La réponse de Kent fonctionnera étant donné quelque chose qui implémente IList<T>. Pour quelque chose que vous n'avez pas, vous devrez construire une liste des choses que vous voulez supprimer. Par exemple:

public static void RemoveWhere<T>(this ICollection<T> self, Func<T, bool> predicate) 
{ 
    var toRemove = self.Where(predicate).ToList(); 

    foreach (var i in toRemove) 
     self.Remove(i); 
}