替换其中一个集合后,Join和SelectMany之间的行为不同

时间:2010-12-27 10:20:00

标签: join linq

我希望有人可以了解(对我而言)两个(结果明智的)平等查询之间出乎意料的行为差异。
一个小程序可以胜过千言万语,所以这里有:

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  
}

现在我知道Join内部使用查找类来优化性能,而且没有太多的知识,我的猜测是它与捕获的变量的组合可能会导致这种行为,但是说我真的理解它,不: )
这是乔尔称之为“漏洞抽象”的一个例子吗?

干杯, 巴特

1 个答案:

答案 0 :(得分:2)

考虑到您在评论中的查询扩展,您实际上几乎就在那里:

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

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

查看每种情况下使用l2的位置。在Join的情况下,l2值会立即传递给方法。 (请记住,该值是对列表的引用,但...更改列表的内容与更改l2的值不同。)稍后更改l2的值不会t会影响Join方法返回的查询所记录的内容。

现在看看SelectManayl2仅用于lambda表达式...所以它是捕获的变量。这意味着无论何时评估lambda表达式,都会使用当时l2 的值...因此它将反映对值的任何更改。