C#中的Select和SelectMany:如何选择以提升速度?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C#中的Select和SelectMany:区别何在,又如何影响速度?



如果你曾经用C#编写过LINQ查询,那么你肯定遇到过两个相似但截然不同的方法:Select和SelectMany。乍一看,两者都是从集合中“选择”某些东西。但魔鬼,一如既往,藏在细节之中。在两者之间做出错误的选择,可能会让你的快速代码变得缓慢而混乱;而正确的选择,则能让你的代码既优雅又迅捷。



在本文中,我们不仅会讲解理论。我们还会进行真实的性能测试,深入探究这两个方法的内部机制,并为你提供一个清晰的决策指南:何时使用Select,何时使用SelectMany。准备好了吗?让我们开始吧!



快速入门:什么是Select和SelectMany?



首先,基础知识。假设你有一个学生列表(List<Student>),每个学生都有一个成绩列表(List<int>)。



Select 是一种“一对一”的投影。对于原始集合中的每个元素,你都会得到一个输出元素。如果你有10个学生,Select将返回一个包含10个元素的集合。



  • 示例:获取所有学生的姓名。
  • 签名: IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)


SelectMany 是一种“一对多”的投影,随后会进行“展平”(flatten)操作。原始集合中的每个元素可以产生多个输出元素,所有这些元素会被合并成一个单一的扁平列表。



  • 示例:获取所有学生的所有成绩(一个单一的扁平数字列表)。
  • 签名: IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)


看起来区别很明显。但陷阱在哪里呢?陷阱在于这些方法在底层是如何工作的,以及新手经常如何混淆它们。



主要区别:输出数据的形状



让我们通过代码示例来巩固这一点。这是我们的模型类:



public class Student{    public string Name { get; set; }    public List<int> Grades { get; set; } = new List<int>();}

// 创建数据var students = new List<Student>{ new Student { Name = "安娜", Grades = new List<int>{ 5, 4, 5 } }, new Student { Name = "鲍里斯", Grades = new List<int>{ 3, 4 } }, new Student { Name = "维卡", Grades = new List<int>{ 5, 5, 5, 4 } }};


示例1:使用Select(不正确)



// 我们想获取所有成绩。但Select会返回一个集合的集合!var allGradesNested = students.Select(s => s.Grades);// 类型:IEnumerable<List<int>> — 内部有三个列表!

foreach (var gradeList in allGradesNested){ foreach (var grade in gradeList) { Console.Write(grade + " "); // 不得不使用嵌套循环 }}// 输出:5 4 5 3 4 5 5 5 4


这能工作,但不方便。你得到的是一个嵌套结构,需要额外进行展平。



示例2:使用SelectMany(正确)



// SelectMany替我们完成了所有工作:它取出内部的集合并将它们“压平”var allGradesFlat = students.SelectMany(s => s.Grades);// 类型:IEnumerable<int> — 一个单一的扁平数字列表!

foreach (var grade in allGradesFlat){ Console.Write(grade + " "); // 无需嵌套循环!}// 输出:5 4 5 3 4 5 5 5 4


感受到区别了吗?

博客

书籍推荐