List <T>.RemoveAll () эффективность / оптимизация компилятора - PullRequest
24 голосов
/ 05 апреля 2019

Что касается эффективности, кто-нибудь знает, достаточно ли умен компилятор, чтобы не создать массив, содержащий 1, 3, 5 для каждой итерации цикла в следующем коде?

var foo = new List<int> { 1, 2, 3, 4, 5 };
foo.RemoveAll(i => new[] { 1, 3, 5 }.Contains(i));

Я предпочитаю его для удобства чтения, но не ради производительности.

Ответы [ 3 ]

13 голосов
/ 05 апреля 2019

Ответ: нет, он не оптимизирует распределение массива

По сути, каждый раз, когда вызывается предикат, он проверяет класс, сгенерированный компилятором, и инициализирует новый массив для вызова * 1003.* (как видите здесь )

private sealed class <>c
{
    public static readonly <>c <>9 = new <>c();

    public static Predicate<int> <>9__0_0;

    internal bool <M>b__0_0(int i)
    {
        // bam!
        int[] obj = new int[3];
        RuntimeHelpers.InitializeArray(obj, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
        return Enumerable.Contains(obj, i);
    }
}
4 голосов
/ 05 апреля 2019

Как уже писал @Michael Randall, похоже, что это невозможно.

Я согласен, что ваш сомнительный код хорошо читается, имея список в методе RemoveAll.Но чтобы иметь экземпляр только один раз, у меня есть три идеи сделать это:

int[] a = null;
foo.RemoveAll(i => (a ?? (a = new[] { 1, 3, 5 })).Contains(i));

Это на самом деле ваше, с небольшим количеством необходимости в внешней переменной.

 foo = foo.Except(new[] { 1, 3, 5 }).ToList();

Это довольно хорошее решение с использованием Linq.

 new List<int>{1, 3, 5}.ForEach(x => foo.Remove(x));


 new[] {1, 3, 5}.Iterate(x => foo.Remove(x));

Это то, что я бы сделал.Почти во всем моем коде у меня есть метод Extension "Iterate", чтобы избежать необходимости в foreach.А также, я не хочу все время «списывать», чтобы сделать .ForEach (..)

static class Extensions
{
    public static void Iterate<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
    {
        foreach (var item in source)
        {
            action.Invoke(item);
        }
    }
}
0 голосов
/ 05 апреля 2019

Поскольку компилятор не настолько умен, мы должны перехитрить его.

var foo = new List<int> { 1, 2, 3, 4, 5 };
var bar = new HashSet<int>() { 1, 3, 5 };
foo.RemoveAll(i => bar.Contains(i));
...