Мне нужно удалить некоторые строки из таблицы данных. Я слышал, что менять коллекцию пока не удается. Поэтому вместо цикла for, в котором я проверяю, удовлетворяет ли строка требованиям к удалению, а затем помечает его как удаленный, я должен сначала перебрать таблицу данных и добавить все строки в список, затем выполнить итерацию по списку и пометить строки для удаления. Каковы причины этого и какие у меня альтернативы (вместо того, чтобы использовать список строк, который я имею в виду)?
Как изменять или удалять элементы из перечислимой коллекции, итерации через нее в С#
Ответ 1
Вы можете удалить элементы из коллекции, если используете простой цикл 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);
}
Ответ 2
Итерация назад через список звучит как лучший подход, потому что, если вы удаляете элемент и другие элементы "попадаете в промежуток", это не имеет значения, потому что вы уже посмотрели на них. Кроме того, вам не нужно беспокоиться о том, что ваша счетная переменная становится больше, чем .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);
}
}
Ответ 3
Взяв код @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);
}
Но seriuosly, в наши дни я бы использовал LINQ:
var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });
l.RemoveAll(n => n % 2 == 0);
Ответ 4
Поскольку вы работаете с 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();
}
}
Вызов удаления в строках изменит их свойство 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
}
}
Удаление строк из коллекции (как в ответе bruno) приведет к поломке адаптера таблицы и, как правило, не будет выполняться с помощью DataTable.
Ответ 5
В цикле while будет обрабатываться следующее:
int i = 0;
while(i < list.Count)
{
if(<codition for removing element met>)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
Ответ 6
решение 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));
}
Ответ 7
Удаление или добавление в список, в то время как итерация через него может сломать его, как вы сказали.
Я часто использовал подход с двумя списками для решения проблемы:
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.
Ответ 8
Когда мне нужно удалить элемент из коллекции, которую я перечисляю, я обычно перечисляю ее в обратном порядке.