107 votes

c # Un moyen intelligent de supprimer des éléments d'une liste <T> en énumérant

J'ai le cas classique de la tentative de suppression d'un élément à partir d'une colletion lors de l'énumération dans une boucle. Voici un exemple:

List<int> myIntCollection=new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);

foreach(int i in myIntCollection)
{
    if (i == 42)
       myIntCollection.Remove(96); //The error is here.

    if (i == 25)
       myIntCollection.Remove(42); //The error is here.

}

Il existe de nombreux modèles qui peuvent être utilisés pour éviter cela, mais aucun d'entre eux semblent avoir une bonne solution:

Modèle 1

Ne supprimez pas à l'intérieur de cette boucle, au lieu de garder une autre "Effacer la Liste", que vous traitez après la boucle principale.

Normalement, c'est une bonne solution, mais dans mon cas, j'ai besoin de l'article pour être disparu instantanément comme "en attente" jusqu'à l'après la boucle principale pour vraiment supprimer l'élément modifie le flux logique de mon code.

Modèle 2

Au lieu de la suppression de l'élément, il suffit de définir un indicateur sur l'élément et le marquer comme étant inactif. Puis ajouter la fonctionnalité de Modèle 1 nettoyer la liste. Ce SERAIT le travail de l'ensemble de mes besoins, mais cela signifie que BEAUCOUP de code devra être modifié afin de vérifier les inactifs indicateur à chaque fois qu'un élément est accessible. C'est beaucoup trop administration, à mon goût.

Le schéma 3

J'ai été informé que l'accès à MyIntCollection.ToArray() au lieu de MyIntCollection permettra de résoudre le problème et me permettre de supprimer l'intérieur de la boucle. Cela ressemble à un mauvais modèle pour moi, ou peut-être que c'est bien?

Le schéma 4

Incorporer l'idée de Modèle 2 en interne au sein d'une classe qui dérive de la Liste. Cette Superliste va gérer les inactifs drapeau, la suppression d'objets après le fait et aussi de ne pas exposer les éléments marqués comme inactifs à l'énumération des consommateurs. Est fondamentalement juste un condensé de tous les idées de Modèle 2 (et subsiquently Schéma 1).

Une classe comme cela existe? quelqu'un aurait-il le code pour cela? Ou est-il un meilleur moyen?

Suggestions s'il vous plaît. Merci à l'avance. ;o)

PRÉCISIONS

La Liste contient de nombreux éléments.

Je vais être la suppression des éléments spécifiques (pas tous).

L'intérieur de la boucle que je vais faire toutes sortes de procédés, l'ajout, la suppression, etc. Donc, la solution doit être assez générique.

Merci encore une fois.

CLARIFICATION 2

J'ai dû ouvrir ce nouveau dû à un léger changement:

L'élément que j'ai besoin de supprimer ne PEUT PAS être l'élément courant dans la boucle. Par exemple, j'ai peut-être sur le point 10 de un 30 de l'élément de boucle et j'ai besoin de supprimer un article 6 ou de l'article 26.

La marche à l'envers dans le tableau ne fonctionne plus à cause de cela ;o(

Exemple de Code mis à jour.

213voto

dlev Points 28160

La meilleure solution consiste généralement à utiliser l' RemoveAll() méthode:

myList.RemoveAll(x => x.SomeProp == "SomeValue");

Ou, si vous avez besoin de certains éléments supprimés:

MyListType[] elems = new[] { elem1, elem2 };
myList.RemoveAll(x => elems.Contains(x));

Cette supposons que votre boucle est uniquement prévu pour l'enlèvement des fins, bien sûr. Si vous ne besoin d'un traitement supplémentaire, la meilleure méthode est généralement d'utiliser un for ou while boucle, car alors vous n'êtes pas à l'aide d'un agent recenseur:

for (int i = myList.Count - 1; i >= 0; i--)
{
    // Do processing here, then...
    if (shouldRemoveCondition)
    {
        myList.RemoveAt(i);
    }
}

Aller vers l'arrière vous permet de ne pas ignorer tous les éléments.

Réponse à Modifier:

Si vous allez à ont apparemment arbitraire des éléments supprimés, la méthode la plus simple pourrait être de simplement garder une trace des éléments que vous souhaitez supprimer, puis de les supprimer tous à la fois d'après. Quelque chose comme ceci:

List<int> toRemove = new List<int>();
foreach (var elem in myList)
{
    // Do some stuff

    // Check for removal
    if (needToRemoveAnElement)
    {
        toRemove.Add(someElement);
    }
}

// Remove everything here
myList.RemoveAll(x => toRemove.Contains(x));

30voto

JaredPar Points 333733

Si vous devez à la fois énumérer un List<T> et le supprimer, je vous suggère simplement d’utiliser une boucle while au lieu d’un foreach

 var index = 0;
while (index < myList.Count) {
  if (someCondition(myList[index])) {
    myList.RemoveAt(index);
  } else {
    index++;
  }
}
 

20voto

D-Jones Points 71

Je sais que ce post est vieux, mais je pensais partager ce qui a fonctionné pour moi.

Créez une copie de la liste pour l'énumération, puis dans chaque boucle, vous pouvez traiter les valeurs copiées et supprimer / ajouter / quoi que ce soit avec la liste source.

 Private Sub RemoveMyObjectsFromMyList(MyList As List(Of MyObject))

    For Each obj As MyObject In MyList.ToList
        If obj.DeterminingValue > 10 Then
            MyList.Remove(obj)
        End If
    Next

End Sub
 

Ma réponse est dans vb.net mais le concept est le même

8voto

Justin Points 42106

Lorsque vous avez besoin de parcourir une liste et de la modifier pendant la boucle, il vaut mieux utiliser une boucle for:

 for (int i = 0; i < myIntCollection.Count; i++)
{
    if (myIntCollection[i] == 42)
    {
        myIntCollection.Remove(i);
        i--;
    }
}
 

Bien sûr, vous devez faire attention, par exemple, je décrémente i chaque fois qu'un élément est supprimé, sinon nous ignorerons les entrées (une autre solution consiste à revenir en arrière dans la liste).

Si vous avez Linq, vous devriez simplement utiliser RemoveAll comme suggéré par dlev.

5voto

James Curran Points 55356

Au fur et à mesure que vous énumérez la liste, ajoutez celui que vous souhaitez GARDER à une nouvelle liste. Ensuite, assignez la nouvelle liste aux myIntCollection

 List<int> myIntCollection=new List<int>();
myIntCollection.Add(42);
List<int> newCollection=new List<int>(myIntCollection.Count);

foreach(int i in myIntCollection)
{
    if (i want to delete this)
        ///
    else
        newCollection.Add(i);
}
myIntCollection = newCollection;
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X