Безопасное удаление DataRow в ForEach
-
22-09-2019 - |
Вопрос
Я не понимаю, почему этот код не работает.
foreach (DataRow dataRow in dataTable.Rows)
{
if (true)
{
dataRow.Delete();
}
}
Решение
Несмотря на то DataRow.Delete
не изменяет состояние коллекции, Документация Майкрософт утверждает, что вы не должны вызывать его во время итерации по коллекции:
Ни удаление, ни удаление не должны вызываться в цикле foreach при проходе по объекту DataRowCollection.Удалить и Удалить не изменяют состояние коллекции.
Лучшим решением обычно является создание отдельной коллекции (например.а List<DataRow>
) элементов, которые вы хотите удалить, а затем удалите их. после вы закончили итерацию.
Это также решение для ситуаций, когда вы хотите удалить элементы из коллекции, поскольку большинство коллекций в .NET не позволяют вам изменять содержимое коллекции во время ее обработки.
Другие советы
Самый безопасный способ – использовать for
петля
for (int i = datatable.Rows.Count - 1; i >= 0; i--)
{
if (true)
{
datatable.Rows[i].Delete();
}
}
Не забудьте AcceptChanges
чтобы удалить все отмеченные строки:
datatable.AcceptChanges();
Вы не можете изменять коллекцию во время ее итерации с помощью 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();
}
Если вы вызываете метод удаления, вам просто нужно вызвать 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
возможно, мой ответ больше не будет полезен.исключение в Foreach с DataRow появляется только в .Net 2.0 и более ранних версиях, причина - описание в msdnhttp://msdn.microsoft.com/en-us/library/system.data.datarow.delete(v=vs.80).aspx
Если RowState строки имеет значение Added, строка удаляется из таблицы.
RowState становится удаленным после использования метода Delete.Он остается удаленным до тех пор, пока вы не вызовете AcceptChanges.
Удаленную строку можно восстановить, вызвав RejectChanges.
чтобы решить эту проблему, вы можете вызвать DataTable.AcceptChanges() перед использованием foreach
foreach (DataRow dataRow in dataTable.Rows)
{
if (true)
{
dataRow.Delete();
}
}
dataTable.AcceptChanges();
Пожалуйста, ознакомьтесь со снимками, чтобы понять, как это работает.
- Просто удалено, но не удалено из DataTable.
- Точка останова перед функцией AcceptChanges().
- После выполнения функции AcceptChanges().
Надеюсь, теперь эта проблема решена.
А Rows
содержимое изменяется во время итерации, если вы удаляете одну строку, что делает итерацию недействительной.
Однако вы можете сначала скопировать строки в коллекцию, а затем перебрать коллекцию и таким образом удалить строки.Это гарантирует, что итерация не будет прервана изменением данных, подлежащих итерации.
Самый простой способ добиться этого — использовать список для сопоставления строк, которые вы хотите удалить, а затем удалить строки вне итерации DataTable.
С#
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);
}
ВБ
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
Есть другая версия (думаю, более простая), которую я только что использовал:
int i=0;
while (i < myDataTable.Rows.Count)
{
if (condition) //should it be deleted?
myDataTable.Rows.RemoveAt(i);
else
i++;
}
Это быстрее.
Только для людей, которые ищут конкретный сценарий, как я, мне нужно было сократить время, и после того, как из каждого ряда извлечено какое -то полезное разобщение, я исключил строку, отмечая как удаленное.
Надеюсь, это поможет кому-то...
foreach (DataRow dataRow in dataTable.Rows)
{
if (dataRow.RowState != DataRowState.Deleted)
{
if (your condition here)
{
dataRow.Delete();
}
}
}
Это касается практически любой коллекции.Если вы попытаетесь удалить элемент во время обхода коллекции, у вас возникнет проблема.Например, если вы удалите строку №3, предыдущая строка №4 станет строкой №3.
Если предметы имеют Count
, вот что я сделал:
int Count = myTable.Rows.Count;
while (Count > 0) // replace condition with myTable.Rows.Count if unconditionally performed on all rows
{
DataRow row = myTable.Rows[0] // or however you want to find your index
// do some work
myTable.Rows.Remove(row);
// if you want to perform a check to break out of while
if (someCondition)
Count = 0;
else
Count = myTable.Rows.Count;
}
Обратите внимание, что там, где объекты имеют .GetXXXX()
коллекция, типа FileInfo
(IIRC), Удаление содержимого предмета в foreach
приемлемо.Одним из решений, которое я рассмотрел, является создание метода расширения, который предоставляет .GetItems()
метод.
Использовать это:
for (int i = 0; i < myDataTable.Rows.Count; i++)
{
myDataTable[i].Delete();
}
Это потому, что это похоже на попытку разобрать ступеньку лестницы, по которой вы поднимаетесь.Проще говоря, вы не можете удалить элемент, который выполняете итерацию.
Поэтому вам следует использовать другой массив для итерации и удаления их из таблицы данных. Rows
свойство.
foreach (DataRow dataRow in dataTable.Select())
{
if (true)
{
dataTable.Rows.Remove(dataRow);
}
}
Конечно, Магенты
Вот как я это сделал и отлично работает
dt = GetStationeryConsolidationDetails(txtRefNo.Text);
int intRows = dt.Rows.Count;
int x = 0;
for (int c = 0; c < intRows; c++)
{
if (dt.Rows[c - x]["DQTY"].ToString() == "0")
{
dt.Rows[c - x].Delete();
dt.AcceptChanges();
x++;
}
}