2010-12-27 4 views
2

2人の(結果的に)等価なクエリ間の予期しない動作上の相違について、誰かが(私に)予期せぬ動作上の違いを明らかにできることを願っています。
小さなプログラムは、千の言葉の価値があることができますので、ここに行く:今セットの1つを置き換えた後、JoinとSelectManyの動作が異なる

static void Main(string[] args) 
{ 
    var l1 = new List<int> { 1, 2, 3 }; 
    var l2 = new List<int> { 2, 3, 4 }; 

    var q1 = // or var q1 = l1.Join(l2, i => i, j => j, (i, j) => i); 
     from i in l1 
     join j in l2 
     on i equals j 
     select i; 

    var q2 = //or var q2 = l1.SelectMany(i => l2.Where(j => i == j)); 
     from i in l1 
     from j in l2 
     where i == j 
     select i; 

    var a1 = q1.ToList(); // 2 and 3, as expected 
    var a2 = q2.ToList(); // 2 and 3, as expected 

    l2.Remove(2); 

    var b1 = q1.ToList(); // only 3, as expected 
    var b2 = q2.ToList(); // only 3, as expected 

    // now here goes, lets replace l2 alltogether. 
    // Afterwards, I expected the same result as q1 delivered... 

    l2 = new List<int> { 2, 3, 4 }; 

    var c1 = q1.ToList(); // only 3 ? Still using the previous reference to l2 ? 
    var c2 = q2.ToList(); // 2 and 3, as expected 
} 

私はそれが内部的に参加し、パフォーマンスを最適化するために、ルックアップクラスを使用し、あまり知識がなくても知っている、私の推測であるとの組み合わせキャプチャされた変数を使用するとこの動作が発生する可能性がありますが、実際にはわかりません:-)
これはJoelが「漏れ抽象」と呼んでいる例ですか?コメントであなたのクエリ拡張を与え

乾杯、 バート

答えて

2

あなたはほとんどそこに実際にしている、:l2は、それぞれの場合に使用されているところで

var q1 = l1.Join(l2, i => i, j => j, (i, j) => i); 

var q2 = l1.SelectMany(i => l2.Where(j => i == j)); 

ルック。 Joinの場合は、l2の値がになります。値はリストの参照ですが、リストの内容を変更することはl2の値を変更するのと同じではありません。l2の値を変更しても、クエリによって返されたクエリには影響しません。 Joinメソッドは覚えています。 l2のみラムダ式で使用されている...それは捕獲変数です:

は今SelectManayを見てください。これは、ラムダ式が評価されるたびに、のその瞬間のl2の値が使用されることを意味します。そのため、値の変更が反映されます。

+0

スポットあり、ありがとうJon! –

関連する問題