8Mは、作成されたクロージャインスタンスの数ではなく、クロージャクラスでメソッドが実行された回数を指していると思います。まず、コードをコンパイルしてみましょう。
class Class2
{
public static void DoSomeWork(foo item, List<bar> bList)
{
var query = bList.Where(x => x.prop1 == item.A && x.prop2 == item.B)
.ToList();
if (query.Any())
DoSomethingElse();
}
static void DoSomethingElse() { }
}
class foo { public int A { get; set; } public int B { get; set; } }
class bar { public int prop1 { get; set; } public int prop2 { get; set; } }
これで、元の「//<---匿名メソッドクロージャを1つだけ呼び出す」を破棄できます。コメント。実際には、匿名メソッドのクロージャは使用されないため.Any()
、リストに内容があるかどうかをチェックするだけです。クロージャは必要ありません。
今; コンパイラで何が起こっているかを示すために、クロージャを手動で書き直してみましょう。
class Class2
{
class ClosureClass
{
public foo item; // yes I'm a public field
public bool Predicate(bar x)
{
return x.prop1 == item.A && x.prop2 == item.B;
}
}
public static void DoSomeWork(foo item, List<bar> bList)
{
var ctx = new ClosureClass { item = item };
var query = bList.Where(ctx.Predicate).ToList();
if (query.Any()) {
DoSomethingElse();
}
}
static void DoSomethingElse() { }
}
ClosureClass
ごとに1が作成されていることがわかります。これは、キャプチャされた変数( )のみがメソッドレベルでスコープさDoSomeWork
れる方法に直接マップされます。述語()は1回だけ取得item
されますが、内のすべての項目に対して呼び出されます。したがって、実際、2000*4000はメソッドへの800万回の呼び出しです。ただし、メソッドへの8Mの呼び出しは必ずしも遅いとは限りません。ctx.Predicate
bList
でも!最大の問題は、存在を確認するためだけに新しいリストを作成していることだと思います。あなたはそれを必要としません。以前に移動することで、コードをはるかに効率的にすることができます。Any
if (bList.Any(x => x.prop1 == item.A && x.prop2 == item.B)) {
DoSomethingElse();
}
これにより、一致が見つかるまで十分な回数だけ述語が呼び出されるようになりました。これは、すべての述語よりも少ないと予想されます。また、リストが不必要にいっぱいになることもありません。
今; はい、これを手動で行う方が少し効率的です。
bool haveMatch = false;
foreach(var x in bList) {
if(x.prop1 == item.A && x.prop2 == item.B) {
haveMatch = true;
break;
}
}
if(haveMatch) {
DoSomethingElse();
}
ただし、との間のこの変更は重大な違いAny
でforeach
はないことに注意してください。重要な違いは、とを削除したToList()
ことです。「一致するものが既に見つかった場合でも、読み続けてください」。使用Any(predicate)
法ははるかに簡潔で読みやすいなどです。これは通常、パフォーマンスの問題ではなく、ここにもあるとは思えません。