发布于 2026-01-06 2 阅读
0

LINQ SelectMany 深入解析

LINQ SelectMany 深入解析

在本文中,我将介绍 LINQSelectMany方法的各种重载和用法。

SelectManyGroupBy在很多方面,它与我上次在本系列文章中介绍的 LINQ 方法正好相反。LINQGroupBy方法将单个集合转换为多个子集合,而SelectManyLINQ 方法则将子集合扁平化为一个合并的集合。

那么,这种扁平化处理究竟有何用处呢?

基本 SelectMany 操作

想象一下你有一系列书籍。每本书中可能包含一个或多个角色(想想人,而不是字母)。

如果我想列举我所有的藏书并识别每个角色——无论出自哪本书——这或多或少就是SelectMany以收藏为基础所做的。

换句话说,SelectMany将集合中每个项目的可枚举属性映射到一个扁平列表中。

这段代码最简单的形式如下所示:

var people = books.SelectMany(b => b.Characters);
Enter fullscreen mode Exit fullscreen mode

此操作将返回一个字符列表,其格式可能如下所示(为便于理解,添加了注释):

[
  // Characters from Sphere
  "Harry",
  "Norman",
  "Beth",
  "Jerry",
  // Characters from Jurassic Park
  "Malcolm",
  "Grant",
  "Satler",
  "Nedry",
  "Hammond",
  "Gennaro",
  "Tim",
  "Lex"
]
Enter fullscreen mode Exit fullscreen mode

请注意,生成的集合是一个单一的扁平集合,而不是多个集合组。这SelectMany几乎是该方法的逆操作GroupBy

我们提供的用于识别嵌套集合的函数SelectMany称为集合选择器

添加结果选择器

LINQ 还提供了一个额外的重载,它为我们提供了一个名为结果选择器的东西。

结果选择器是一个简单的函数,它将集合中原本要返回的单个节点转换为其他类型的节点。它通过一个函数来实现这一点,该函数接收父集合(沿用之前的类比,可以想象成一本书)以及该集合中的子节点(沿用之前的类比,可以想象成一个角色)。

看看它的样子,它使用(b, c)以下参数作为函数的输入,该函数返回一个新的匿名类型,其中包含一本书和一个角色:

最终形成的资料集现在为每个角色提供了更多背景信息,如下方这个简短示例所示:

[
  {
    "Book": "Sphere",
    "Character": "Harry"
  },
  // Some results omitted
  {
    "Book": "Jurassic Park",
    "Character": "Lex"
  }
]
Enter fullscreen mode Exit fullscreen mode

请注意,您完全不需要返回对象。由于示例中的字符都是字符串,我们可以像下面这样轻松地进行一些字符串格式化操作:

这段更简单的代码可以生成更简洁、更易读的输出结果:

[
  "Harry (Sphere)",
  "Norman (Sphere)",
  "Beth (Sphere)",
  "Jerry (Sphere)",
  "Malcolm (Jurassic Park)",
  "Grant (Jurassic Park)",
  "Satler (Jurassic Park)",
  "Nedry (Jurassic Park)",
  "Hammond (Jurassic Park)",
  "Gennaro (Jurassic Park)",
  "Tim (Jurassic Park)",
  "Lex (Jurassic Park)"
]
Enter fullscreen mode Exit fullscreen mode

很酷吧?

正如我们所看到的,它不仅可以SelectMany将嵌套集合扁平化为单个集合,还可以根据需要将这些集合中的对象转换或映射为不同的对象。

基于索引的过载

有时您需要知道某个项在源集合中的索引。这种情况比较少见,通常涉及将两个不同的数据源连接起来。

对于这种情况,LINQ 为我们目前讨论的两种方法都提供了重载。每个重载都允许你向集合选择器添加一个函数参数,该参数将接收该集合的基于整数的索引。

为了更直观地了解其外观,请看以下示例:

这里,b集合选择器中的参数对应于Book对象,而i参数则是集合中从零开始的索引。我们characters通过索引从集合中获取字符列表,并SelectMany能够对结果对象使用其结果选择器。

再次强调,这是一种不太常用的重载方法,但当数据分散在多个集合中时,这种方法会很有帮助。

结语

在我看来,SelectMany这比它的逆运算更有用GroupBy

我强烈建议SelectMany您在需要将嵌套列表扁平化为单个集合时考虑使用这种方法。

此外,能够在单个方法调用中扁平化和转换集合的能力非常高效(以牺牲可读性为代价),并且可以减少将后续 LINQ 调用链接在一起以转换结果集合的需要。

最终,我认为SelectMany在处理嵌套集合时,你会因为其实用价值而偶尔但可靠地使用它。映射函数可能不太需要,但在某些关键场景下仍然很重要。

如果本文有任何令人困惑的地方,请告诉我,或者查阅微软关于方法组的官方文档。

如果你发现了本文未提及的用途SelectMany,我很乐意将其添加到我的技巧库中。请留言告诉我你的发现。

文章《LINQ SelectMany 深度解析》最初发表于Kill All Defects网站。

文章来源:https://dev.to/integerman/linq-selectmany-in-deep-37af