我有一个看起来像这样的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变量设置为之后添加了额外的人。
答案 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
}