【问题标题】:Unexpected results in Linq queryLinq 查询中的意外结果
【发布时间】:2010-11-25 19:38:33
【问题描述】:

我有一个看起来像这样的 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 变量是在 添加额外的人之前设置的。

【问题讨论】:

    标签: c# winforms linq .net-3.5


    【解决方案1】:

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

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

    这是 Linq 的重要组成部分,是为了提高效率。延迟执行可以节省大量时间和资源 b/c 可以提前中止查询,因此,如果我们不需要尾部,我们就浪费了时间和内存。此外,如果结果集非常庞大,则效率低下。 (想想 slurp 文件而不是使用 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() 时刻完整执行。您的原始列表保留在 theTeam 中,而您的“额外”人员则在人员 IEnumerable 中。

    【讨论】:

      【解决方案2】:

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

      正在发生的事情是 LINQ 创建了一个IEnumerable&lt;Person&gt;,它将遍历您的“人员”集合。但是,这不是您的集合的副本 - 它会根据要求进行枚举。

      稍后,当您执行“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
      }
      

      【讨论】:

      • 如果您在查询之后在 myPosse 上调用 .Count() 会不会更好?这比将其转换为列表要好: var myPosse = (from p1 in people select p1).ToList(); myPosse.Count();
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 2019-03-06
      • 2019-05-04
      相关资源
      最近更新 更多