Мне нужно удалить некоторые строки из таблицы данных. Я слышал, что менять коллекцию пока не удается. Поэтому вместо цикла for, в котором я проверяю, удовлетворяет ли строка требованиям к удалению, а затем помечает его как удаленный, я должен сначала перебрать таблицу данных и добавить все строки в список, затем выполнить итерацию по списку и пометить строки для удаления. Каковы причины этого и какие у меня альтернативы (вместо того, чтобы использовать список строк, который я имею в виду) ?.Как изменить или удалить элементы из перечислимой коллекции, итерации через нее в C#
ответ
Вы можете удалить элементы из коллекции, если используете простой цикл 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);
}
Это неверно, так как не все элементы будут проверены. Если вы удалите элемент в i, тогда элемент в i + 1 станет i. Когда i затем увеличивается на следующий цикл, он пропускает элемент, который только что заменил удаленный элемент (надеюсь, что это имеет смысл). –
Я исправил это. Спасибо Энди за пощечину. Но я хотел сказать, что вы можете изменить коллекцию с циклом for. –
Могу ли я использовать l [i] .Delete(), так как removeat создаст проблемы с адаптером таблицы в процедуре обновления (строки будут удалены из таблицы вместо того, чтобы просто быть помечены как удаленные). – kjv
Через некоторое время цикл будет справиться с этим:
int i = 0;
while(i < list.Count)
{
if(<codition for removing element met>)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
Это решение сталкивается с той же проблемой, что и выше, ваши индексы будут отключены, если вы удалите элемент. – Element
Нет, они не будут, поскольку индекс только увеличивается, когда элемент не удаляется. –
Удаление или добавление в список в то время как переборе он может разорвать его, как вы сказали.
Я часто использовал два списка подход к решению проблемы:
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.
Этот подход на самом деле хорош и потокобезопасен. –
Возможно, вы можете отредактировать его как код C#? –
Поскольку вы работаете с 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 свойство удалён, но оставить удаленные строки в таблице. Если вам все еще нужно работать с этой таблицей, прежде чем сохранять изменения на сервере (например, если вы хотите отобразить содержимое таблицы за вычетом удаленных строк), вам нужно проверить RowState каждой строки, как вы итерации через нее :
foreach (DataRow row in dt.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
// this row has not been deleted - go ahead and show it
}
}
Удаление строк из коллекции (как в ответ Брюно) сломается адаптер таблицы, и как правило, не будет сделано с DataTable.
Итерация назад через список звучит как лучший подход, потому что, если вы удалите элемент и другие элементы «попадаете в промежуток», это не имеет значения, потому что вы уже посмотрели на них. Кроме того, вам не нужно беспокоиться о том, что ваша переменная счетчика становится больше, чем значение .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);
}
}
Когда мне нужно удалить элемент из коллекции, я перечисляющем я обычно перечислить его в обратном направлении.
Принимая код @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);
+1 для реализации мощности LINQ для чего-то подобного. Почему дела идут так долго, когда вам не нужно – BenAlabaster
, почему бы не определить список l как Список
+ 1 для RemoveAll. Примечание. RemoveAll не является строго LINQ. Он доступен для любого класса List в .NET 2.0 без поддержки LINQ. –
chakrit также может быть использована, если вы ориентируетесь .NET 2.0 (без LINQ/лямбда-выражений), используя делегат, а не лямбда-выражение:
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));
}
Я отредактировал заголовок, чтобы было легче найти этот вопрос. Был обман, который я закрыл раньше, но я вижу, как пользователь мог пропустить этот вопрос со старым названием. –