web-dev-qa-db-fra.com

Comment modifier ou supprimer des éléments d'une collection iEnumerable tout en itération à travers elle dans C #

Je dois supprimer des lignes d'une table de données. J'ai entendu dire que ce n'est pas correct de changer de collection tout en itérant à travers elle. Donc, au lieu d'une boucle dans laquelle je vérifie si une ligne rencontre les exigences de suppression, puis la marquez comme supprimée, je devrais d'abord itération via la table de données et ajouter toutes les lignes dans une liste, puis itérer dans la liste et marquer les lignes pour les suppressions. Quelles sont les raisons pour les raisons et quelles alternatives ai-je (au lieu d'utiliser la liste des lignes que je veux dire) ?.

38
kjv

Vous pouvez supprimer des éléments d'une collection si vous utilisez une simple boucle for.

Jetez un coup d'œil à cet exemple:

        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);
        }
18
bruno conde

Itération en arrière à travers la liste sonne comme une meilleure approche, car si vous supprimez un élément et d'autres éléments, "tomber dans l'écart", cela n'a pas d'importance car vous avez déjà examiné ceux-ci. En outre, vous n'avez pas à vous soucier de votre compteur variable devenant plus grand que le .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);
            }
        }
88
Michael Stum

Prendre le code @bruno, je le ferais en arrière.

Parce que lorsque vous déménagez en arrière, les indices de tableau manquants n'interfèrent pas avec l'ordre de votre boucle.

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

Mais Seriuosly, ces jours-ci, j'utiliserais Linq:

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

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

Puisque vous travaillez avec un jeu de données et avez besoin de pouvoir persister les modifications du serveur avec un adaptateur de table (voir commentaires), voici un exemple de la façon dont vous devez supprimer des lignes:

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

L'appel de la suppression sur les lignes changera leur propriété de rangée à supprimer, mais laissez les lignes supprimées dans la table. Si vous avez toujours besoin de travailler avec cette table avant de persister les modifications de retour sur le serveur (comme si vous souhaitez afficher le contenu de la table moins les lignes supprimées), vous devez vérifier la rampe de chaque ligne pendant que vous êtes itération de cela comme ça :

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

Enlever les lignes de la collection (comme dans la réponse de Bruno) enfreindre l'adaptateur de table et ne devrait généralement pas être effectué avec un jeu de données.

5
MusiGenesis

Une boucle tandis que la boucle gérerait ceci:

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

la solution de Chakrit peut également être utilisée si vous ciblez .NET 2.0 (pas d'expressions LINQ/Lambda) à l'aide d'un délégué plutôt que d'une expression 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));
}
3
Ian Hopkins

Suppression ou ajout à la liste tandis que itération à travers elle peut le casser, comme vous l'avez dit.

J'ai souvent utilisé une approche de deux liste pour résoudre le problème:

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.
2
Philluminati

Quand j'ai besoin de supprimer un article d'une collection que je suis énumérable, je l'énorme habituellement en sens inverse.

0
Adrian