[转载]C# LINQ 快速入门实战指南,建议收藏学习!
LINQ介绍
LINQ语言集成查询是一系列直接将查询功能集成到 C# 语言的技术统称。数据查询历来都表示为简单的字符串,没有编译时类型检查或 IntelliSense 支持。此外,需要针对每种类型的数据源了解不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等。然而,LINQ的出现改变了这一现状,它使查询成为了与类、方法和事件同等重要的高级语言构造。通过LINQ,开发者能够以声明性的方式查询和操作数据,极大地提高了开发效率和代码的可维护性。
LINQ具有以下特性
- 强类型:编译时验证查询逻辑,减少运行时错误。
- 延迟执行:LINQ查询通常是延迟执行的,即查询表达式本身不会立即执行,直到实际遍历结果时才触发查询。使用
ToList()、ToArray()、ToDictionary()、FirstOrDefault()等方法可立即执行。 - 支持多种数据源:LINQ可以用于查询多种数据源,如
LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to Entities(Entity Framework)等。 -
LINQ中常用方法
操作示例数据
1 public class StudentInfo 2 { 3 public int StudentID { get; set; } 4 public string StudentName { get; set; } 5 public DateTime Birthday { get; set; } 6 public int ClassID { get; set; } 7 public string Address { get; set; } 8 public List<Course> Courses { get; set; } = new List<Course>(); 9 } 10 11 public class Course 12 { 13 public int CourseID { get; set; } 14 public string CourseName { get; set; } 15 } 16 17 static List<StudentInfo> students = new List<StudentInfo> 18 { 19 new StudentInfo 20 { 21 StudentID=1, 22 StudentName="大姚", 23 Birthday=Convert.ToDateTime("1997-10-25"), 24 ClassID=101, 25 Courses = new List<Course> 26 { 27 new Course { CourseID = 101, CourseName = "语文" }, 28 new Course { CourseID = 102, CourseName = "数学" } 29 } 30 }, 31 new StudentInfo 32 { 33 StudentID=2, 34 StudentName="李四", 35 Birthday=Convert.ToDateTime("1998-10-25"), 36 ClassID=101, 37 Courses = new List<Course> 38 { 39 new Course { CourseID = 101, CourseName = "语文" }, 40 new Course { CourseID = 102, CourseName = "数学" } 41 } 42 }, 43 new StudentInfo 44 { 45 StudentID=3, 46 StudentName="王五", 47 Birthday=Convert.ToDateTime("1999-10-25"), 48 ClassID=102, 49 Address="广州", 50 Courses = new List<Course> 51 { 52 new Course { CourseID = 101, CourseName = "语文" }, 53 new Course { CourseID = 102, CourseName = "数学" } 54 } 55 }, 56 new StudentInfo 57 { 58 StudentID=4, 59 StudentName="时光者", 60 Birthday=Convert.ToDateTime("1999-11-25"), 61 ClassID=102, 62 Address="深圳" , 63 Courses = new List<Course> 64 { 65 new Course { CourseID = 104, CourseName = "历史" }, 66 new Course { CourseID = 103, CourseName = "地理" } 67 } 68 } 69 };
基本查询方法
- Where:用于过滤集合中的元素,通过一个谓词(返回布尔值的条件)筛选集合中的元素,生成一个仅包含满足条件元素的新序列。
- Select:用于将集合中的每个元素投影(转换)为新序列。
- SelectMany:用于将多个集合(嵌套集合,如集合的集合)
展平为一个集合。
1 var femaleStudents = students.Where(s => s.StudentName == "时光者"); 2 var studentNames = students.Select(s => s.StudentName); 3 4 // 使用SelectMany展平所有学生的课程列表 5 var allCourses = students.SelectMany(student => student.Courses).ToList(); 6 7 // 输出所有课程的名称 8 foreach (var course in allCourses) 9 { 10 Console.WriteLine(course.CourseName); 11 }
转换方法
- ToList:将实现了
IEnumerable<T>接口的集合转换为一个List<T>类型的对象,属于将集合转换为特定类型列表的方法。 - ToArray:将一个实现了
IEnumerable<T>接口的集合转换为一个数组,属于将集合转换为数组类型的方法。 - ToDictionary:将一个
IEnumerable<T>集合转换为一个Dictionary<TKey,TValue>键值对集合(字典)的方法,注意 ToDictionary 要求键唯一,否则抛出异常。 - ToLookup:将一个
IEnumerable<T>集合转换为一个泛型Lookup<TKey,TElement>,Lookup<TKey,TElement>一个一对多字典,用于将键映射到值的集合。
var studentList = students.ToList(); var studentArray = students.ToArray(); var studentDictionary = students.ToDictionary(s => s.StudentID, s => s.StudentName); var studentLookup = students.ToLookup(s => s.ClassID, s => s.StudentName);
元素操作方法
- First:返回集合中的第一个元素。
- FirstOrDefault:返回集合中的第一个元素,如果集合中未找到该元素,则返回默认值。
- Single:返回集合中的单个元素,如果集合中未找到该元素或包含多个元素则抛出异常。
- SingleOrDefault:返回集合中的单个元素,如果集合中未找到该元素,则返回默认值;如果该集合中包含多个元素,此方法将引发异常。
- Last:返回集合中的最后一个元素。
- LastOrDefault:返回集合中的最后一个元素,如果集合中未找到该元素,则返回默认值。
- ElementAt:返回集合中指定索引处的元素。
- ElementAtOrDefault:返回集合中指定索引处的元素,如果索引超出范围则返回默认值。
- DefaultIfEmpty:如果集合为空,则返回一个包含默认值的集合。
1 var firstStudent = students.First(); 2 var firstAdult = students.FirstOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18)); 3 var onlyWangWu = students.Single(s => s.StudentName == "王五"); 4 var wangWuOrDefault = students.SingleOrDefault(s => s.StudentName == "王六"); 5 var lastStudent = students.Last(); 6 var lastAdult = students.LastOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18)); 7 var secondStudent = students.ElementAt(1); 8 var tenthStudentOrDefault = students.ElementAtOrDefault(9); 9 var nonEmptyStudents = students.DefaultIfEmpty(new StudentInfo { StudentID = 0, StudentName = "默认Student", Address = "默认" });
排序方法
- OrderBy:用于对集合进行升序排序。
- OrderByDescending:用于对集合进行降序排序。
- ThenBy:按升序对集合中的元素执行后续排序。
- ThenByDescending:按降序对集合中的元素执行后续排序。
var sortedByBirthdayAsc = students.OrderBy(s => s.Birthday); var sortedByClassIDDesc = students.OrderByDescending(s => s.ClassID); var sortedByNameThenClassID = students.OrderBy(s => s.StudentName).ThenBy(s => s.ClassID); var sortedThenByDescending = students.OrderBy(s => s.StudentName).ThenBy(s => s.ClassID).ThenByDescending(x => x.Birthday);
聚合方法
- Count:返回集合中的元素数量。
- Sum:返回集合中数值类型元素的和。
- Average:返回集合中数值类型元素的平均值。
- Min:返回集合中的最小值。
- Max:返回集合中的最大值。
- Aggregate:对集合进行自定义聚合操作。
1 int studentCount = students.Count(); 2 int totalClassID = students.Sum(s => s.ClassID); 3 double averageAge = students.Average(s => DateTime.Now.Year - s.Birthday.Year); 4 int minClassID = students.Min(s => s.ClassID); 5 int maxClassID = students.Max(s => s.ClassID); 6 string concatenatedNames = students.Aggregate("", (acc, s) => acc == "" ? s.StudentName : acc + ", " + s.StudentName);
集合操作方法
- Distinct:返回集合中的唯一元素(去除重复项)。
- Union:返回两个集合的并集(合并后去重)。
- Intersect:返回两个集合的交集(共有的唯一元素)。
- Except:返回在第一个集合中存在但不在第二个集合中存在的元素(取集合的差集)。
- Concat:连接两个集合,返回一个新的序列(保留所有元素,包括重复项)。
var uniqueClassIDs = students.Select(s => s.ClassID).Distinct(); var unionClassIDs = uniqueClassIDs.Union(new[] { 103, 104 }); var intersectClassIDs = uniqueClassIDs.Intersect(new[] { 101, 103 }); var exceptClassIDs = uniqueClassIDs.Except(new[] { 101 }); var concatClassIDs = uniqueClassIDs.Concat(new[] { 103, 104 });
分组与连接方法
- GroupBy:对集合中的元素进行分组。
- Join:基于匹配键对两个集合的元素进行关联。
- GroupJoin:基于键值等同性将两个集合的元素进行关联,并对结果进行分组。
1 var groupedByClassID = students.GroupBy(s => s.ClassID); 2 3 foreach (var group in groupedByClassID) 4 { 5 Console.WriteLine($"班级ID: {group.Key}"); 6 foreach (var student in group) 7 { 8 Console.WriteLine($" 学生姓名: {student.StudentName}"); 9 } 10 } 11 12 // 连接两个集合(内连接查询) 13 var otherStudent = new List<StudentInfo> 14 { 15 new StudentInfo 16 { 17 StudentID=4, 18 StudentName="摇一摇", 19 Birthday=Convert.ToDateTime("1997-10-25"), 20 ClassID=101, 21 Courses = new List<Course> 22 { 23 new Course { CourseID = 101, CourseName = "语文" }, 24 new Course { CourseID = 102, CourseName = "数学" } 25 } 26 } 27 }; 28 29 var listJoin = students.Join( 30 otherStudent, // 要连接的第二个集合 31 s1 => s1.StudentID, // 从第一个集合中提取键 32 s2 => s2.StudentID, // 从第二个集合中提取键 33 (s1, s2) => new // 结果选择器,指定如何从两个匹配元素创建结果 34 { 35 StudentID = s1.StudentID, 36 StudentName = s1.StudentName, 37 Birthday = s1.Birthday, 38 ClassID = s1.ClassID, 39 Address = s1.Address, 40 Courses = s1.Courses, 41 OtherStudentName = s2.StudentName 42 }); 43 44 //使用 GroupJoin 方法实现两个集合的左连接(Left Join) 45 //目标:获取所有课程及选修学生(即使无人选修也要显示课程) 46 var courseStudentGroups = courses.GroupJoin( 47 students.SelectMany( 48 student => student.Courses, 49 (student, course) => new { Student = student, Course = course } 50 ), 51 course => course.CourseID, 52 studentCoursePair => studentCoursePair.Course.CourseID, 53 // 结果投影:生成课程名称及对应的学生列表 54 (course, matchedStudents) => new 55 { 56 CourseName = course.CourseName, 57 Students = matchedStudents 58 .Select(pair => pair.Student.StudentName) 59 .DefaultIfEmpty("(无学生)") 60 .ToList() 61 } 62 ).ToList(); 63 64 // 输出结果 65 foreach (var group in courseStudentGroups) 66 { 67 Console.WriteLine("-------------------"); 68 Console.WriteLine($"课程:{group.CourseName}"); 69 Console.WriteLine($"选修学生:{string.Join(", ", group.Students)}"); 70 Console.WriteLine("-------------------"); 71 }
跳过与获取指定数量的元素(常用作分页)
- Skip:用于跳过集合中指定数量的元素,并返回剩余的元素序列。
- Take:用于从集合的开头获取指定数量的元素,并返回一个新的序列。
1 var skippedStudents = students.Skip(1); 2 var takenStudents = students.Take(2); 3 4 //数据分页查询(Skip + Take) 5 int pageNumber = 2; 6 int pageSize = 10; 7 var pagedUsers = skippedStudents 8 .OrderBy(u => u.ClassID) // 必须排序 9 .Skip((pageNumber - 1) * pageSize) 10 .Take(pageSize) 11 .ToList();
条件判断方法
- All:判断集合中的所有元素是否都满足条件。
- Any:判断集合中是否包含元素或存在元素满足指定条件。
- Contains:用于判断集合中是否包含指定的元素。
1 bool allAdults = students.All(s => s.Birthday <= DateTime.Now.AddYears(-18)); 2 bool anyAdults = students.Any(s => s.Birthday <= DateTime.Now.AddYears(-18)); 3 bool containsWangWu = students.Contains(students.First(s => s.StudentName == "王五"));
更多方法查询

查询语法
LINQ提供了类似于SQL的查询语法,允许开发者以几乎相同的方式对不同类型的数据源进行查询。查询语法使用from、where、select、orderby等关键字。
1 var querySyntaxResult = from student in students 2 where student.ClassID == 101 3 orderby student.StudentName ascending 4 select student; 5 6 Console.WriteLine("查询语法结果:"); 7 foreach (var student in querySyntaxResult) 8 { 9 Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}"); 10 }
查询关键字:
- from: 指定数据源和范围变量(类似于迭代变量)。
- where: 基于由逻辑 AND 和 OR 运算符(&& 或 ||)分隔的一个或多个布尔表达式筛选源元素。
- select: 指定执行查询时,所返回序列中元素的类型和形状。
- group: 根据指定的密钥值对查询结果分组。
- into: 提供可作为对 join、group 或 select 子句结果引用的标识符(简单理解用于将配对的结果收集到一个临时序列)。
- orderby: 根据元素类型的默认比较器对查询结果进行升序或降序排序。
- join: 基于两个指定匹配条件间的相等比较而联接两个数据源(简单理解根据指定的键将两个序列中的元素配对)。
- let: 引入范围变量,在查询表达式中存储子表达式结果。
- in: join子句中的上下文关键字。
- on: join子句中的上下文关键字。
- equals: join子句中的上下文关键字。
- by: group 子句中的上下文关键字。
- ascending: orderby子句中的上下文关键字。
- descending: orderby子句中的上下文关键字。
方法语法
方法语法也称为扩展方法语法,使用点号“.”和一系列扩展方法来构建查询。
1 var methodSyntaxResult = students 2 .Where(student => student.ClassID == 101) 3 .OrderBy(student => student.StudentName) 4 .ToList(); 5 6 7 Console.WriteLine("方法语法结果:"); 8 foreach (var student in methodSyntaxResult) 9 { 10 Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}"); 11 }
混合查询和方法语法
var mixedResult = (from student in students where student.ClassID == 101 where student.Courses.Any(course => course.CourseName == "数学") orderby student.StudentName ascending select student) .Take(2) .ToList(); // 输出结果 Console.WriteLine("混合查询结果:"); foreach (var student in mixedResult) { Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}"); }
参考文章
- https://learn.microsoft.com/zh-cn/dotnet/csharp/linq
- https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/query-keywords
转载:https://cloud.tencent.com/developer/article/2513974

浙公网安备 33010602011771号