Linq查询中出现意外结果

时间:2009-09-05 02:15:42

标签: c# winforms linq .net-3.5

我有一个看起来像这样的Linq查询:

var myPosse = from p1 in people
             select p1;
label1.Text = "All my peeps:" + Environment.NewLine;
foreach (Person p in myPosse)
{
    this.label1.Text += p.ToString() + Environment.NewLine;
}

这给了我很好的结果。

但是当我做这样的事情时:

var myPosse = from p1 in people
             select p1;
label1.Text = "All my peeps:" + Environment.NewLine;
people.Add(new Person{FirstName="Don", LastName="Cash"});
foreach (Person p in myPosse)
{
    this.label1.Text += p.ToString() + Environment.NewLine;
}

我有'额外'的家伙!这是怎么回事?我的Linq变量设置为之后添加了额外的人。

2 个答案:

答案 0 :(得分:7)

这是因为延迟执行是Linq的一个主要特征。

var中存储的内容实际上不是结果集。实际上是运行查询的潜在。将值分配给变量时,不会运行查询。它只在需要时运行,并且一点一点地运行。

这是Linq的重要组成部分,是为了提高效率。延迟执行节省了大量的时间和资源b / c可以提前中止查询,因此,如果我们不需要尾端,我们就浪费了时间和内存。此外,如果结果集是巨大的,那么它是低效的。 (想想啜饮文件而不是用例如perl或PHP流式传输文件)。

你不能强迫立即执行,但这是一个近似的技巧。

var myPosse = from p1 in people
             select p1;
List<Person> theTeam = myPosse.ToList();
label1.Text = "All my peeps:" + Environment.NewLine;
people.Add(new Person{FirstName="Don", LastName="Cash"});
foreach (Person p in theTeam)
{
    this.label1.Text += p.ToString() + Environment.NewLine;
}

请注意“ToList()”方法。在这种情况下,您的Linq查询将在.ToList()时完整执行。您的原始列表保留在团队中,而您在IEnumerable中拥有“额外”人员。

答案 1 :(得分:3)

当您遍历大多数(但不是全部)LINQ扩展方法的结果时,在您使用它们之前,实际上不会计算结果。这是由于deferred execution

正在发生的事情是LINQ创建了一个IEnumerable<Person>,它将遍历您的“人物”集合。但是,这不是您的收藏的副本 - 它将按要求进行枚举。

当你进行“foreach”循环时,它实际上开始迭代人员集合 - 使用人们就像那时。这意味着你会得到额外的人。

您可以通过使用将创建数据副本的LINQ扩展来避免这种情况:

var myPosse = (from p1 in people
         select p1).ToList();  // This makes a copy in a new list...
label1.Text = "All my peeps:" + Environment.NewLine;
people.Add(new Person{FirstName="Don", LastName="Cash"});
foreach (Person p in myPosse)
{
    this.label1.Text += p.ToString() + Environment.NewLine;
    // Don Cash won't show up now, since it wasn't in the original list when the copy was made
}