Domanda

Duplicato

Modifica Di Una Raccolta Di Iterare Attraverso Di Essa


Qualcuno ha un bel modello mi permettesse di aggirare l'impossibilità di rimuovere gli oggetti, mentre I loop attraverso un enumerabile di raccolta (ad esempio, un IList o KeyValuePairs in un dizionario)

Per esempio, il seguente non riesce, come si modifica l'Elenco enumerato durante il foreach

foreach (MyObject myObject in MyListOfMyObjects)
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

In passato ho utilizzato due metodi.

Ho sostituito il foreach con una rovesciata per il ciclo (in modo da non modificare la indici sono loop su se rimuove un oggetto).

Ho provato anche la memorizzazione di una nuova collezione di oggetti per rimuovere all'interno di ciclo, un ciclo che la raccolta e rimosso gli oggetti da collezione originale.

Funzionano bene, ma né si sente bello, e mi chiedevo se qualcuno ha una di più elegante soluzione al problema

È stato utile?

Soluzione

Esiste un utile List<T>.RemoveAll(Predicate<T> match) metodo che penso sia progettato per questo: http : //msdn.microsoft.com/en-us/library/wdka673a.aspx

Altri suggerimenti

È un po 'semplice, ma quando ho intenzione di eliminare elementi da un IEnumerable / IList di solito ne faccio una copia:

foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

Non è il modo più efficiente per farlo, ma è facile da leggere. Ottimizzazione prematura e tutto il resto.

Fare l'inverso, la creazione di un nuovo elenco:

List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
     if (!condition) myFilteredList.Add(myObject);
}

Quindi utilizzare il nuovo elenco ovunque è necessario.

È inoltre possibile utilizzare un'espressione di LINQ facilmente, di nuovo, inversing la condizione.Questo ha il vantaggio di non creare una nuova struttura, ma anche le insidie di essere un pigro enumerabile:

var myFilteredList = from myObject in myListOfMyObjects
                     where !condition
                     select myObject;

Tuttavia, se avete davvero bisogno di rimuovere gli elementi dalla lista, io in genere uso la "creazione di un nuovo elenco, quindi ribadire e rimuovere" approccio.

Mi sono appena imbattuto in questo post e ho pensato di condividere.

void RemoveAll(object condition)  
{

    bool found = false;

    foreach(object thisObject in objects)    
    {

        if (condition)    
        {    
            objects.Remove(thisObject);

            found = true;

            break; //exit loop    
        }     
     }

    // Call again recursively

    if (found) RemoveAll(condition);

}

Non mi piace l'idea del ciclo inverso, poiché funziona solo su determinate strutture di dati.

In generale, utilizzerei la seconda tecnica e accumulerei gli elementi da eliminare in una raccolta separata da eliminare. Se la cancellazione può far sì che gli iterati esistenti vengano invalidati (come accadrà ad esempio con qualsiasi raccolta di alberi bilanciati), non vedo un modo per aggirare questo.

L'unica altra tecnica che ho usato occasionalmente è quella di riavviare l'intera iterazione quando trovi il primo elemento da eliminare. Se lo fai senza trovare alcun elemento da eliminare, la funzione è terminata. Ciò è inefficiente, ma a volte necessario se l'eliminazione di un elemento dalla raccolta può modificare l'insieme di elementi che devono essere eliminati.

Ho un dizionario e desidero eliminare tutti i valori. Quando viene eliminato, ogni valore viene rimosso dal dizionario, creando il problema in discussione. Ho fatto quanto segue:

foreach (var o in dictionary.Values.ToList())
{
  o.Dispose();
}

Ora apprezzo che questo potrebbe essere morto, ma il modo in cui lo faccio sempre è:

foreach (MyObject myObject in MyListOfMyObjects)
{

if (condition) MyListOfMyObjects.Remove (myObject);

break;

}

L'oggetto viene rimosso e quindi esce il ciclo, viola!

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