効率に関しては、コンパイラがnotを含む配列を作成して1, 3, 5
次のコードのループの反復ごとに?
var foo = new List<int> { 1, 2, 3, 4, 5 };
foo.RemoveAll(i => new[] { 1, 3, 5 }.Contains(i));
私は読みやすさのためにそれを好みますが、パフォーマンスのためではありません。
答えは、配列の割り当てを最適化しないということではありません
基本的に、述語が呼び出されるたびに、コンパイラが生成したクラスと照合して、新しい配列を初期化し、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メソッドにリストがあることに同意します。しかし、インスタンスを一度だけ持つために、私はそれを行うための3つのアイデアを持っています。
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));
これは私がすることです。ほぼすべてのコードで、foreachの必要性を回避するために、拡張メソッドを "反復"しています。また、.ForEach(..)を作成するために常にすべてを「toList」したくありません。
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));