Безопасное удаление DataRow в ForEach - PullRequest
38 голосов
/ 26 февраля 2010

Я не понимаю, почему этот код не работает.

foreach (DataRow dataRow in dataTable.Rows)
{
    if (true)
    {
        dataRow.Delete();
    }
}

Ответы [ 15 ]

47 голосов
/ 22 июня 2010

Самый безопасный способ - используйте for петля

for (int i = datatable.Rows.Count - 1; i >= 0; i--) 
{
    if (true)
    {
        datatable.Rows[i].Delete();
    }
}

Не забудьте AcceptChanges удалить все отмеченные строки:

datatable.AcceptChanges();
32 голосов
/ 26 февраля 2010

Даже если DataRow.Delete не изменяет состояние коллекции, Документация Microsoft гласит, что вы не должны вызывать ее при переборе по коллекции:

Ни Delete, ни Remove не должны вызываться в цикле foreach во время итерации объекта DataRowCollection. Удалить или Удалить изменить состояние коллекции.

Лучшее решение обычно - создать отдельную коллекцию (например, List<DataRow>) элементов, которые вы хотите удалить, а затем удалить их после того, как вы закончили итерацию.

Это также решение для ситуаций, когда вы хотите удалить элементы из коллекции, так как большинство коллекций в .NET не позволяют вам изменять содержимое коллекции, пока вы выполняете ее итерацию.

19 голосов
/ 26 февраля 2010

Вы не можете изменить коллекцию, пока выполняете ее, используя оператор foreach.

Вы можете попробовать что-то подобное:

List<DataRow> deletedRows = new List<DataRow>();

foreach (DataRow dataRow in dataTable.Rows)
{
    if(true) deletedRows.Add(dataRow);
}

foreach(DataRow dataRow in deletedRows)
{
    dataRow.Delete();
}
8 голосов
/ 07 января 2012

Если вы вызываете метод delete, вам просто нужно вызвать AcceptChanges() в таблице, которую вы изменяете, после цикла foreach.

foreach (DataRow dataRow in dataTable.Rows)
{
    if (true)
    {
        dataRow.Delete();
    }
}

dataTable.AcceptChanges();

Метод delete просто помечает строку для удаления.

http://msdn.microsoft.com/en-us/library/system.data.datarow.delete%28v=VS.90%29.aspx

4 голосов
/ 28 мая 2012

может быть мой ответ бесполезен. Исключение в Foreach с DataRow появляется только в .Net 2.0 и более ранних версиях, причина - описание в msdn http://msdn.microsoft.com/en-us/library/system.data.datarow.delete(v=vs.80).aspx

Если RowState строки добавлен, строка удаляется из таблицы.

RowState становится удаленным после использования метода Delete. Он остается удаленным до тех пор, пока вы не вызовете AcceptChanges.

Удаленную строку можно восстановить, вызвав RejectChanges.

для решения этой проблемы вы можете вызвать DataTable.AcceptChanges () перед использованием foreach

2 голосов
/ 09 октября 2014
foreach (DataRow dataRow in dataTable.Rows)
{
    if (true)
    {
        dataRow.Delete();
    }
}

dataTable.AcceptChanges();

Пожалуйста, ознакомьтесь со снимками, чтобы понять, как оно работает.

  1. Только что удалено, но не удалено из таблицы данных.

enter image description here

  1. Точка останова перед функцией AcceptChanges (). enter image description here
  2. После выполнения функции AcceptChanges (). enter image description here

Надеюсь, эта проблема решена сейчас.

1 голос
/ 11 ноября 2016

Только для людей, которые ищут определенный сценарий, как я, Мне нужно было сократить время, затрачиваемое на извлечение полезной информации из каждой строки, и я исключил ее, пометив как удаленную.

Надеюсь, это поможет кому-то ...

foreach (DataRow dataRow in dataTable.Rows)
{
    if (dataRow.RowState != DataRowState.Deleted)
    {
        if (your condition here)
        {
            dataRow.Delete();
        }
    }
}
1 голос
/ 17 июля 2015

Есть другая версия (я думаю, что проще), которую я только что использовал:

int i=0;
while (i < myDataTable.Rows.Count)
{
    if (condition)  //should it be deleted?
        myDataTable.Rows.RemoveAt(i);
    else
        i++;
}

Это быстрее.

1 голос
/ 08 февраля 2015

Самый простой способ добиться этого - использовать список для сопоставления строк, которые вы хотите удалить, а затем удалять строки вне итерации DataTable.

C #

    List<DataRow> rowsWantToDelete= new List<DataRow>();

    foreach (DataRow dr in dt.Rows)
    {
        if(/*Your condition*/)
        {
            rowsWantToDelete.Add(dr);
        }
    }

    foreach(DataRow dr in rowsWantToDelete)
    {
        dt.Rows.Remove(dr);
    }

VB

Dim rowsWantToDelete As New List(Of DataRow)

For Each dr As DataRow In dt
    If 'Your condition' Then
        rowsWantToDelete .Add(dr)
    End If
Next

For Each dr As DataRow In rowsWantToDelete 
    dt.Rows.Remove(dr)
Next
1 голос
/ 26 февраля 2010

Содержимое Rows изменяется во время итерации, если вы удаляете одну строку, что делает итерацию недействительной.

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

...