Как условно удалить объекты из набора.NET

16
задан Lee D 17 March 2009 в 09:58
поделиться

5 ответов

Для List<T>, это уже существует, как RemoveAll(Predicate<T>). По сути, я предложил бы, чтобы Вы сохранили имя (позволяющий знакомство и приоритет).

В основном, Вы не можете удалить при итерации. Существует две общих опции:

  • используют основанное на индексаторе повторение (for), и удаление
  • буферизуют объекты, чтобы удалить, и удалить после foreach (поскольку Вы уже сделали)

Так, возможно:

public static void RemoveAll<T>(this IList<T> list, Func<T, bool> predicate) {
    for (int i = 0; i < list.Count; i++) {
        if (predicate(list[i])) {
            list.RemoveAt(i--);
        }
    }
}

Или в более общем плане для любого ICollection<T>:

public static void RemoveAll<T>(this ICollection<T> collection, Func<T, bool> predicate) {
    T element;

    for (int i = 0; i < collection.Count; i++) {
        element = collection.ElementAt(i);
        if (predicate(element)) {
            collection.Remove(element);
            i--;
        }
    }
}

Этот подход имеет преимущество предотвращения большого количества дополнительных копий списка.

38
ответ дан 30 November 2019 в 15:52
поделиться

Как Marc сказал, List<T>.RemoveAll() способ пойти для списков.

я удивлен, что Ваша вторая версия не работала, хотя, учитывая, что у Вас есть вызов к ToList() после эти Where() вызов. Без эти ToList() вызов это, конечно, имело бы смысл (потому что это будет оценено лениво), но это должно быть хорошо как есть. Вы могли показать короткий, но полный пример этого сбоя?

РЕДАКТИРОВАНИЕ: Относительно Вашего комментария в вопросе я все еще не могу заставить это перестать работать. Вот короткое, но завершенный пример, который работает:

using System;
using System.Collections.Generic;
using System.Linq;

public class Staff
{
    public int StaffId;
}

public static class Extensions
{
    public static void RemoveWhere<T>(this ICollection<T> Coll,
                                      Func<T, bool> Criteria)
    {
        List<T> forRemoval = Coll.Where(Criteria).ToList();

        foreach (T obj in forRemoval)
        {
            Coll.Remove(obj);
        }
    }
}

class Test
{
    static void Main(string[] args)
    {
        List<Staff> mockStaff = new List<Staff>
        {
            new Staff { StaffId = 3 },
            new Staff { StaffId = 7 }
        };

       Staff newStaff = new Staff{StaffId = 5};
       mockStaff.Add(newStaff);
       mockStaff.RemoveWhere(s => s.StaffId == 5);

       Console.WriteLine(mockStaff.Count);
    }
}

, Если Вы могли бы обеспечить подобное завершенный пример, который перестал работать, я уверен, что мы можем разработать причину.

7
ответ дан 30 November 2019 в 15:52
поделиться

Я просто протестировал его, и Ваш второй метод хорошо работает (как это должно). Что-то еще должно идти не так, как надо, можно ли предоставить немного кода кода, который показывает проблему?

List<int> ints = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

ints.RemoveWhere(i => i > 5);
foreach (int i in ints)
{
    Console.WriteLine(i);
}

Добирается:

1
2
3
4
5
1
ответ дан 30 November 2019 в 15:52
поделиться

Я просто попробовал Ваш второй пример, и это, кажется, хорошо работает:

Collection<int> col = new Collection<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
col.RemoveWhere(x => x % 2 != 0);

foreach (var x in col)
    Console.WriteLine(x);
Console.ReadLine();

я не получил исключение.

1
ответ дан 30 November 2019 в 15:52
поделиться

Другая версия Marcs RemoveAll:

public static void RemoveAll<T>(this IList<T> list, Func<T, bool> predicate)
{
    int count = list.Count;
    for (int i = count-1; i > -1; i--)
    {
        if (predicate(list[i]))
        {
            list.RemoveAt(i);
        }
    }
}
0
ответ дан 30 November 2019 в 15:52
поделиться