Как изменить или удалить элементы из перечисляемой коллекции, проходя по ней в C # - PullRequest
38 голосов
/ 21 ноября 2008

Я должен удалить несколько строк из таблицы данных. Я слышал, что нельзя менять коллекцию во время итерации. Таким образом, вместо цикла for, в котором я проверяю, соответствует ли строка требованиям удаления, а затем помечаю ее как удаленную, я должен сначала выполнить итерацию по таблице данных и добавить все строки в список, а затем выполнить итерацию по списку и отметить строки для удаления. Каковы причины этого и какие у меня есть альтернативы (вместо того, чтобы использовать список строк, который я имею в виду)?.

Ответы [ 8 ]

88 голосов
/ 21 ноября 2008

Итерация в обратном направлении по списку звучит как лучший подход, потому что, если вы удаляете элемент, а другие элементы «попадают в пропасть», это не имеет значения, потому что вы уже просмотрели их. Кроме того, вам не нужно беспокоиться о том, что ваша переменная-счетчик станет больше, чем .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);
            }
        }
33 голосов
/ 22 ноября 2008

Взяв код @bruno, я бы сделал это задом наперед.

Поскольку при перемещении назад индексы отсутствующего массива не влияют на порядок вашего цикла.

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

Но серьезно, в эти дни я бы использовал LINQ:

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

l.RemoveAll(n => n % 2 == 0);
18 голосов
/ 21 ноября 2008

Вы можете удалить элементы из коллекции, если используете простой цикл for.

Взгляните на этот пример:

        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);
        }
5 голосов
/ 21 ноября 2008

Поскольку вы работаете с DataTable и должны иметь возможность сохранять любые изменения обратно на сервер с помощью адаптера таблицы (см. Комментарии), вот пример того, как вы должны удалять строки:

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

Вызов delete для строк изменит их свойство RowState на Deleted, но оставит удаленные строки в таблице. Если вам все еще нужно поработать с этой таблицей, прежде чем сохранять изменения обратно на сервер (например, если вы хотите отобразить содержимое таблицы за вычетом удаленных строк), вам нужно проверить RowState каждой строки, пока вы выполняете ее итерацию следующим образом :

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

Удаление строк из коллекции (как в ответе Бруно) сломает адаптер таблицы, и, как правило, этого не следует делать с DataTable.

3 голосов
/ 24 декабря 2008
Решение

chakrit также можно использовать, если вы ориентируетесь на .NET 2.0 (без выражений LINQ / 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 голосов
/ 21 ноября 2008

Цикл while будет обрабатывать это:

int i = 0;
while(i < list.Count)
{
    if(<codition for removing element met>)
    {
        list.RemoveAt(i);
    }
    else
    {
        i++;
    }
}
2 голосов
/ 21 ноября 2008

Удаление или добавление в список во время его итерации может привести к его поломке, как вы сказали.

Я часто использовал два списка подходов для решения проблемы:

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.
0 голосов
/ 22 ноября 2008

Когда мне нужно удалить элемент из коллекции, которую я перечисляю, я обычно перечисляю ее в обратном порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...