2009-07-14 19 views
8

Zasadniczo chciałbym usunąć element z listy podczas pętli foreach. Wiem, że jest to możliwe, gdy używamy pętli for, ale do innych celów, chciałbym wiedzieć, czy jest to możliwe przy użyciu pętli foreach.Modyfikowanie kolekcji przy użyciu pętli foreach w C#

W Pythonie możemy to osiągnąć w następujący sposób:

a = [1, 2, 3, 4, 5, 6, 7, 8, 9] 

for i in a: 
    print i 

    if i == 1: 
     a.pop(1) 

To daje następujący wynik

>>>1 
3 
4 
5 
6 
7 
8 
9 

Ale kiedy robi coś podobnego w C#, otrzymuję InvalidOperationException, zastanawiałem się, jeśli był sposób obejścia tego, , nie używając po prostu pętli for.

Kod w C#, który kiedyś, gdy wyjątek:

static void Main(string[] args) 
    { 
    List<string> MyList = new List<string>(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9"}); 

    foreach (string Item in MyList) 
    { 
    if (MyList.IndexOf(Item) == 0) 
     { 
     MyList.RemoveAt(1); 
     } 

    Console.WriteLine(Item); 
    } 
    } 

góry dzięki

Odpowiedz

25

Nie mogę tego zrobić. Od docs dla IEnumerator<T>:

Numerator zachowuje ważność tak długo, jak kolekcja pozostaje niezmieniona. Jeśli do kolekcji zostaną wprowadzone zmiany , takie jak dodawanie, modyfikowanie lub usuwanie elementów , moduł wyliczający zostanie nieodwracalnie unieważniony, a jego zachowanie zostanie niezdefiniowane.

Alternatywy to:

  • budować nową listę elementów, które można usunąć, a następnie usunąć je wszystkie potem
  • Użyć zwykłej pętli „for” i upewnij się, że jesteś ostrożny nie dzieje nad tym samym elementem dwa razy lub brakuje go. (Powiedziałeś, że nie chcesz tego robić, ale to, co starasz się zrobić po prostu nie zadziała.)
  • zbudować nową kolekcję zawierającą tylko te elementy, które chcesz zachować

ostatni z tych alternatyw jest LINQ-podobnego rozwiązania, w których mają zazwyczaj napisać:

var newList = oldList.Where(x => ShouldBeRetained(x)).ToList(); 

(. gdzie ShouldBeRetained jest logika cokolwiek chcesz, oczywiście) wezwanie do ToList() jest konieczne tylko, jeśli rzeczywiście chcesz to na liście. Prowadzi to do bardziej deklaratywnego kodu, który jest często łatwiejszy do odczytania. Nie mogę łatwo zgadnąć, co ma zrobić twoja oryginalna pętla (obecnie wydaje się to dość dziwne), podczas gdy jeśli potrafisz wyrazić logikę wyłącznie w kategoriach przedmiotu, może być dużo jaśniej.

+0

Głównie zastanawiałem się, czy coś przegapiłem gdzieś z instancją foreach, ale jeśli nie jest to możliwe, przynajmniej jest to teraz potwierdzone! Dziękuję za odpowiedź – ThePower

1

Z pewnością nie możesz zmienić kolekcji w żaden sposób, gdy używasz pętli foreach.

Możesz użyć pętli for i zarządzać indeksem dla siebie lub zrobić kopię kolekcji i podczas zapętlania oryginału, usuwać elementy z kopii, które są takie same jak elementy w oryginale.

W obu przypadkach nie jest to tak jasne ani wygodne :).

6

Jeśli wszystko, co potrzebne jest, aby usunąć wszystkie elementy, które spełniają warunek można użyć metody List<T>.RemoveAll:

List<string> MyList = new List<string>(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" }); 
MyList.RemoveAll(item => item == "1"); 

pamiętać, że ta modyfikuje wstępną listę.