【问题标题】:SelectMany nested in parent's lambda vs SelectMany after SelectManySelectMany 嵌套在父级的 lambda 与 SelectMany 之后的 SelectMany
【发布时间】:2020-02-17 17:38:44
【问题描述】:

这两个示例给出了相同的结果,但不同的语法告诉我它们以完全不同的方式执行。区别在哪里?应该首选哪种方式?

第一个

continents
    .SelectMany(continent => continent.Countries)
    .SelectMany(country => country.Cities)

第二次

continents
    .SelectMany(continent => 
        continent.Countries.SelectMany(country => country.Cities))

编辑:我们不要谈论 IEnumerable 的延迟执行,因为它在这里并不重要。请假设每个查询都以 .ToList() 结尾。

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    应该首选哪种方式? 块引用

    任何。因为您正在使用延迟执行的 IEnumerable 方法(了解更多:MSDN)

    【讨论】:

      【解决方案2】:

      第二个解决方案不需要第二个 SelectMany。 如果您需要将 IEnumerable 转换为 List,这很重要(SelectMany 比 Select 占用更多资源)。 正如 Alexbogs 在他的回答中所说,如果您只使用 IEnumerable 就没有关系。

      我的建议:

      continents.SelectMany(continent => continent.Countries.Select(country => country.Cities))
      

      【讨论】:

      • 如果在两个示例的末尾添加 ToList(),您能否扩展您的答案?
      • @Jacek 如果您将 .ToList() 添加到 3 个示例并使用虚构数据计算时间,您会注意到城市列表的检索有显着差异(速度快 50 到 100 倍,具体取决于数据)。我远不是 Select() 和 SelectMany() 如何工作的专家,但我认为 SelectMany() 更耗费资源和时间。
      【解决方案3】:

      我对其他答案之一感到困惑。您的查询都返回一个简单的城市列表,如果这就是您所需要的,那么您如何链接SelectManys 并不重要。我认为这是唯一正确的答案。

      将第二个SelectMany 替换为Select 会显着改变查询结果。它返回按国家分组的嵌套城市列表。所以我不确定这如何回答你的问题。

      在其他情况下,括号的放置方式很重要。在第一个查询中,continents.SelectMany(continent => continent.Countries) 部分列出了国家,之后,大陆超出了范围。在第二个查询中,大陆可以一直保持在范围内。

      差异在查询语法中得到了最好的体现。假设您要列出所有大洲的国家和城市名称。在查询语法中:

      from continent in Continents
      from country in continent.Countries
      from city in country.Cities
      select
      new
      {
          country.CountryName,
          city.CityName
      }
      

      在方法语法中,这相当于:

      Continents.SelectMany(continent => continent.Countries)
         .SelectMany(country => country.Cities,
              (country, city) => new 
              { 
                  CountryName = country.CountryName, 
                  CityName = city.CityName
              } )
      

      如您所见,它在第一个 SelectMany 的右括号之后添加了一个 Selectmany,就像在您的第一个查询中一样。范围内只能保留国家/地区。

      如果您想列出国家名称和城市名称之外的大陆名称,您可以在方法语法中使用第二个查询的重载:

      Continents.SelectMany
      (
          continent => continent.Countries.SelectMany
          (
              c => c.Cities
              , (country, city) => new { country, city }
          ), (continent, x) => new
          {
              continent.ContinentName,
              x.country.CountryName,
              x.city.CityName
          }
      )
      

      同样,查询语法看起来更友好:

      from continent in Continents
      from country in continent.Countries
      from city in country.Cities
      select
      new
      {
          continent.ContinentName,
          country.CountryName,
          city.CityName
      }
      

      但是编译的方法语法在传递中间匿名类型时有点不同:

      Continents.SelectMany(continent => continent.Countries,
            (continent, country) => new { continent = continent, country = country } )
         .SelectMany(x => x.country.Cities,(x, city) =>
               new
               {
                   ContinentName = x.continent.ContinentName,
                   CountryName = x.country.CountryName,
                   CityName = city.CityName
               }
         )
      

      这是您第一个查询的变体。

      所以如何链接SelectManys 取决于最终结果中您需要哪些实体。查询语法的好处是编译器会为您计算出来。

      【讨论】:

      • 感谢您的回答。这接近我的预期。我需要更改问题,因为我想要更多细节。
      • 我没有得到你的答案。您的最后一个 sn-p 与我的第二个查询不同。它还在 SelectMany 之后链接 SelectMany,就像在第一个实例中一样。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多