开发者

Different behaviour between Join and SelectMany after replacing one of the sets

I hope that someone can shed a light on the (to me) unexpected behavioral difference between the two (result wise) equal queries.

A small program can be worth a thousand words, so here goes :

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  
}

Now I kno开发者_如何学Gow that Join internally uses a lookup class to optimize performance, and without too much knowledge, my guess is that the combination of that with captured variables might cause this behavior, but to say I really understand it, no :-)

Is this an example of what Joel calls "a leaky abstraction" ?

Cheers, Bart


You're actually nearly there, given your query expansions in the comments:

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

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

Look at where l2 is used in each case. In the Join case, the value of l2 is passed into the method immediately. (Remember that the value is a reference to the list though... changing the contents of the list isn't the same as changing the value of l2.) Changing the value of l2 later doesn't affect what the query returned by the Join method remembers.

Now look at SelectManay: l2 is only used in the lambda expression... so it's a captured variable. That means that whenever the lambda expression is evaluated, the value of l2 at that moment in time is used... so it will reflect any changes to the value.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜