Como modificar ou itens de exclusão de uma coleção IEnumerable enquanto iterar-lo em C #

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

  •  08-07-2019
  •  | 
  •  

Pergunta

Eu tenho que apagar algumas linhas de uma tabela de dados. Ouvi dizer que não é ok para mudar uma coleção durante a iteração através dele. Então, ao invés de um loop em que eu verificar se uma linha atende às demandas para exclusão e, em seguida, marcá-lo como excluído, eu deveria primeiro iterate através da tabela de dados e adicionar todas as linhas em uma lista, em seguida, percorrer a lista e marca as linhas para exclusões. Quais são as razões para isso, e que alternativas que eu tenho (em vez de usar as linhas da lista Eu quero dizer?).

Foi útil?

Solução

Você pode remover elementos de uma coleção, se você usar um loop for simples.

Dê uma olhada neste exemplo:

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

Outras dicas

iteração para trás através dos sons lista como uma abordagem melhor, porque se você remover um elemento e outros elementos "cair no fosso", isso não importa, porque você já olhou para aqueles. Além disso, você não precisa se preocupar com a sua variável de contador se tornar maior do que o .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);
            }
        }

Tomar @bruno código, eu faria isso para trás.

Porque quando você se mover para trás, os índices de matriz falta não interferem com o fim do seu ciclo.

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

Mas seriuosly, estes dias, eu usaria LINQ:

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

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

Uma vez que você está trabalhando com um DataTable e necessidade de ser capaz de persistir quaisquer alterações de volta para o servidor com um adaptador de tabela (ver comentários), aqui está um exemplo de como você deve excluir linhas:

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

Chamada de exclusão nas linhas vai mudar sua propriedade RowState de excluídos, mas deixar as linhas excluídas na tabela. Se você ainda precisa de trabalho com esta tabela antes persistem alterações de volta para o servidor (como se você deseja exibir o conteúdo da tabela menos as linhas excluídas), você precisa verificar o RowState de cada linha como você está iteração através dele como esta :

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

A remoção linhas da coleção (como na resposta de Bruno) vai quebrar o adaptador de tabela, e não deve, em geral, ser feito com um DataTable.

A while loop iria lidar com isso:

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

A solução da chakrit também pode ser usado se você está direcionando .NET 2.0 (sem expressões LINQ / lambda) usando um delegado em vez de uma expressão 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));
}

apagar ou adicionar à lista enquanto iterar ele pode quebrá-lo, como você disse.

I usado frequentemente uma abordagem em duas lista para resolver o 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 eu preciso remover um item de uma coleção que estou enumerando eu costumo enumerá-lo em sentido inverso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top