Safely Removing DataRow In ForEach

2019-01-22 11:35发布

问题:

I don't understand why this code does not work.

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

回答1:

Most collections in .NET don't allow you to change the contents of the collection while you're iterating over it. From the docs for IEnumerator:

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException. If the collection is modified between MoveNext and Current, Current returns the element that it is set to, even if the enumerator is already invalidated.

The best solution is usually to create a separate collection (e.g. a List<DataRow>) of items you want to remove, and then remove them after you've finished iterating.



回答2:

Safest way - use for loop

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

Don't forget to AcceptChanges to remove all marked rows:

datatable.AcceptChanges();


回答3:

You cannot modify a collection while you're iterating on it using a foreach statement.

you can try something like that :

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

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

foreach(DataRow dataRow in deletedRows)
{
    dataRow.Delete();
}


回答4:

If you call the delete method you just have to call AcceptChanges() on the table you are modifying, after the foreach loop.

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

dataTable.AcceptChanges();

The delete method simply marks the row for deletion.

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



回答5:

may be my answer not longer useful. exception throw in Foreach with DataRow only appear in .Net 2.0 and earlier, the reason is description at msdn http://msdn.microsoft.com/en-us/library/system.data.datarow.delete(v=vs.80).aspx

If the RowState of the row is Added, the row is removed from the table.

The RowState becomes Deleted after you use the Delete method. It remains Deleted until you call AcceptChanges.

A deleted row can be undeleted by invoking RejectChanges.

to pass this problem you can call DataTable.AcceptChanges() before using foreach



回答6:

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

dataTable.AcceptChanges();

Please Refer the snaps to understatnd the working of it.

  1. Just Deleted but not removed from the DataTable.

  1. Break point before AcceptChanges() Function.
  2. After Eexecuting AcceptChanges() Function.

I Hope this issue resolved now.



回答7:

The Rows content changes while you are iterating if you delete one row, which renders the iteration invalid.

You can, however, copy the rows into a collection first and then iterate over the collection and delete the rows that way. This makes sure that the iteration is not interrupted by changing data to be iterated.



回答8:

The easiest way to achieve this by using a List to map rows you want to delete and then delete rows outside DataTable iteration.

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


回答9:

There is an other version of it (I think an easier) what I just used:

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

This faster.



回答10:

Only for people who are looking for specific scenario like me, I needed to shorten time taken, and once some useful infomation is extracted from each row, I excluded the row by marking as deleted.

Hope this help someone...

foreach (DataRow dataRow in dataTable.Rows)
{
    if (dataRow.RowState != DataRowState.Deleted)
    {
        if (your condition here)
        {
            dataRow.Delete();
        }
    }
}


回答11:

This applies to pretty much any collection. If you try to delete an item while looping through the collection, you will have a problem. For example, if you delete row #3, then the previous row #4 becomes row #3.



回答12:

Where items have a Count, this is what I have done:

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

Note, that where objects have a .GetXXXX() collection, like FileInfo (IIRC), deleting item contents in a foreach is acceptable. One solution I have considered is creating an extension method that provides a .GetItems() method.



回答13:

Use this:

for (int i = 0; i < myDataTable.Rows.Count; i++)

{

 myDataTable[i].Delete();

}


回答14:

This is because it looks like try to disassemble the stair of staircase that you climb. Simply, you cannot remove an item you iterate.

Therefore you should use different array to iterate and remove them from datatable Rows property.

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


回答15:

Sure Magents

This is how I did it and works fine

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