List<T>.RemoveAll()效率/编译优化

List<T>.RemoveAll() efficiency / compiler optimisation

关于效率,有谁知道编译器是否足够聪明而不是为每个创建包含1, 3, 5的数组以下代码中的循环迭代?

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

我更喜欢它是为了可读性,而不是为了性能。

答案是否定的,它没有优化数组的分配

基本上,每次调用谓词时,它都会检查编译器生成的 class 并初始化一个新数组来调用 Contains(如您所见 here

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

正如@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));

这是我会做的事情。在我几乎所有的代码中,我都有我的扩展方法 "Iterate" 来避免对 foreach 的需要。而且,我不想一直 "toList" 一切来制作 .ForEach(..)

static class Extensions
{
    public static void Iterate<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
    {
        foreach (var item in source)
        {
            action.Invoke(item);
        }
    }
}

既然编译器没有那么聪明,我们就必须智取他。

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