Come modificare o eliminare elementi da una collezione enumerabile mentre si sta iterando in C #

Devo cancellare alcune righe da una tabella di dati. Ho sentito dire che non è giusto cambiare una collezione mentre si scorre attraverso di essa. Quindi, invece di un ciclo for in cui controllo se una riga soddisfa le richieste di cancellazione e poi la contrassegno come eliminata, dovrei prima ripetere la tabella dei dati e aggiungere tutte le righe in un elenco, quindi scorrere l’elenco e il segno le righe per le eliminazioni. Quali sono le ragioni di ciò e quali alternative ho (invece di usare la lista delle righe intendo)?

È ansible rimuovere elementi da una raccolta se si utilizza un ciclo for semplice.

Dai un’occhiata a questo esempio:

  var l = new List(); l.Add(0); l.Add(1); l.Add(2); l.Add(3); l.Add(4); l.Add(5); l.Add(6); for (int i = 0; i < l.Count; i++) { if (l[i] % 2 == 0) { l.RemoveAt(i); i--; } } foreach (var i in l) { Console.WriteLine(i); } 

Iterare all’indietro attraverso l’elenco sembra un approccio migliore, perché se rimuovi un elemento e altri elementi “cadi nel vuoto”, non importa, perché li hai già guardati. Inoltre, non devi preoccuparti che la tua variabile contatore diventi più grande di .Count.

  List test = new List(); test.Add(1); test.Add(2); test.Add(3); test.Add(4); test.Add(5); test.Add(6); test.Add(7); test.Add(8); for (int i = test.Count-1; i > -1; i--) { if(someCondition){ test.RemoveAt(i); } } 

Prendendo il codice @bruno, lo farei all’indietro.

Perché quando si sposta all’indietro, gli indici di array mancanti non interferiscono con l’ordine del loop.

 var l = new List(new int[] { 0, 1, 2, 3, 4, 5, 6 }); for (int i = l.Count - 1; i >= 0; i--) if (l[i] % 2 == 0) l.RemoveAt(i); foreach (var i in l) { Console.WriteLine(i); } 

Ma seriamente, in questi giorni, userei LINQ:

 var l = new List(new int[] { 0, 1, 2, 3, 4, 5, 6 }); l.RemoveAll(n => n % 2 == 0); 

Dato che stai lavorando con un DataTable e devi essere in grado di mantenere qualsiasi modifica sul server con un adattatore da tavolo (vedi commenti), ecco un esempio di come dovresti eliminare le righe:

 DataTable dt; // remove all rows where the last name starts with "B" foreach (DataRow row in dt.Rows) { if (row["LASTNAME"].ToString().StartsWith("B")) { // mark the row for deletion: row.Delete(); } } 

La chiamata di eliminazione sulle righe modificherà la proprietà RowState su Deleted, ma lascerà le righe eliminate nella tabella. Se hai ancora bisogno di lavorare con questa tabella prima di persistere delle modifiche sul server (ad esempio se vuoi visualizzare il contenuto della tabella meno le righe eliminate), devi controllare il RowState di ogni riga mentre stai filando attraverso di esso in questo modo :

 foreach (DataRow row in dt.Rows) { if (row.RowState != DataRowState.Deleted) { // this row has not been deleted - go ahead and show it } } 

La rimozione delle righe dalla raccolta (come nella risposta di Bruno) interromperà l’adattatore della tabella e in genere non dovrebbe essere eseguito con un DataTable.

Un ciclo while gestirà questo:

 int i = 0; while(i < list.Count) { if() { list.RemoveAt(i); } else { i++; } } 

La soluzione di chakrit può essere utilizzata anche se si sta utilizzando .NET 2.0 (senza espressioni LINQ / lambda) utilizzando un delegato anziché un’espressione lambda:

 public bool IsMatch(int item) { return (item % 3 == 1); // put whatever condition you want here } public void RemoveMatching() { List x = new List(); x.RemoveAll(new Predicate(IsMatch)); } 

Cancellare o aggiungere alla lista mentre scorre attraverso di essa può romperlo, come hai detto tu.

Ho spesso utilizzato un approccio a due liste per risolvere il problema:

 ArrayList matches = new ArrayList(); //second list for MyObject obj in my_list { if (obj.property == value_i_care_about) matches.addLast(obj); } //now modify for MyObject m in matches { my_list.remove(m); //use second list to delete from first list } //finished. 

Quando ho bisogno di rimuovere un object da una collezione che sto enumerando, di solito lo enumero al contrario.