Come modificare o eliminare elementi da una raccolta enumerabile mentre si scorre attraverso di essa in C #

StackOverflow https://stackoverflow.com/questions/308466

  •  08-07-2019
  •  | 
  •  

Domanda

Devo eliminare alcune righe da una tabella di dati. Ho sentito che non va bene cambiare una raccolta mentre si scorre attraverso di essa. Quindi, invece di un ciclo for in cui controllo se una riga soddisfa le richieste di eliminazione e quindi la contrassegno come eliminata, dovrei prima scorrere la tabella dei dati e aggiungere tutte le righe in un elenco, quindi scorrere la lista e contrassegnare le righe per le eliminazioni. Quali sono i motivi di ciò e quali alternative ho (invece di usare l'elenco di righe intendo)?

È stato utile?

Soluzione

Puoi rimuovere elementi da una raccolta se usi un semplice ciclo per .

Dai un'occhiata a questo esempio:

        var l = new List<int>();

        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);
        }

Altri suggerimenti

Iterare all'indietro attraverso l'elenco sembra un approccio migliore, perché se rimuovi un elemento e altri elementi "cadi nello spazio", non importa perché hai già guardato quelli. Inoltre, non devi preoccuparti che la tua variabile counter diventi più grande di .Count.

        List<int> test = new List<int>();
        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 al contrario.

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

var l = new List<int>(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<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });

l.RemoveAll(n => n % 2 == 0);

Dato che stai lavorando con una DataTable e devi essere in grado di persistere su qualsiasi modifica al server con un adattatore per tabelle (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 all'eliminazione nelle righe modificherà la proprietà RowState in Eliminata, ma lascerà le righe eliminate nella tabella. Se hai ancora bisogno di lavorare con questa tabella prima di mantenere persistenti modifiche al server (come se desideri visualizzare i contenuti della tabella meno le righe eliminate), devi controllare lo RowState di ogni riga mentre stai iterando in questo modo 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 di righe dalla raccolta (come nella risposta di Bruno) interromperà l'adattatore della tabella e generalmente non dovrebbe essere eseguita con una DataTable.

Un ciclo while gestirà questo:

int i = 0;
while(i < list.Count)
{
    if(<codition for removing element met>)
    {
        list.RemoveAt(i);
    }
    else
    {
        i++;
    }
}

La soluzione di chakrit può essere utilizzata anche se si sta prendendo di mira .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<int> x = new List<int>();
    x.RemoveAll(new Predicate<int>(IsMatch));
}

L'eliminazione o l'aggiunta all'elenco durante l'iterazione può interromperlo, come hai detto.

Ho spesso usato un approccio a due elenchi 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 devo rimuovere un elemento da una raccolta che sto enumerando, di solito lo enumerare al contrario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top