C# --- LINQ
- 什么是LINQ
- [Fluent Syntax 和 SQL-Like Query](#Fluent Syntax 和 SQL-Like Query)
- [LINQ Operations](#LINQ Operations)
什么是LINQ
- LINQ的全称为Language Integrated Query, 为各种查询(包括对象查询,数据库查询,XML查询) 提供了统一模型.
- LINQ源于SQL,但比SQL更加强大,更加灵活.
- LINQ可以用类似于SQL的形式对C# Collection中的对象进行查询
- LINQ可以在代码中对数据库进行查询,编程语言和数据库之间配合上的鸿沟,也就是不用再手动拼接SQL字符串去查询数据库.
- 可读性强:LINQ增加了代码的可读性,因此其他开发人员可以很轻松地理解和维护
Fluent Syntax 和 SQL-Like Query
- LINQ 在C#中分为两种主要语法形式
方法语法(Method Syntax / Fluent Syntax)
- 使用 扩展方法(如 Where(), Select())链式调用,直接操作集合或数据源。
- 特点:
基于 Lambda 表达式,灵活且功能全面。
支持所有 LINQ 操作(某些操作仅能通过方法语法实现,如 Distinct())。
可读性取决于链式调用的复杂度。
csharp
var results = students
.Where(s => s.Age > 18)
.OrderBy(s => s.Name)
.Select(s => new { s.Name, s.Score });
2. 查询表达式语法(Query Syntax / SQL-like Syntax)
- 使用类似 SQL 的声明式语法,以 from、where、select 等关键字编写。
- 特点:
语法更接近自然语言,适合复杂查询(如多表连接、分组)。
编译时会转换为方法语法(本质是语法糖)。
部分操作无法直接表达(需结合方法语法,如 Distinct())。
csharp
var results =
from s in students
where s.Age > 18
orderby s.Name
select new { s.Name, s.Score };
混合使用示例
csharp
// 结合查询表达式和方法语法
var query =
(from s in students
where s.Age > 18
select s)
.Distinct()
.ToList();
总结
- 方法语法:灵活、全面,适合大多数场景。
- 查询表达式:提升复杂查询的可读性,尤其适合多表操作。
- 本质一致:两种语法最终殊途同归,按需选择即可!
LINQ Operations
Where(过滤)
c
var students = new List<Student> { /* 学生对象列表 */ };
// 找到年龄大于 20 的学生
var result = students.Where(s => s.Age > 20);
Select(投影)
c
// 提取所有学生的姓名
var names = students.Select(s => s.Name);
OrderBy / OrderByDescending(排序)
csharp
// 按年龄升序排序
var ordered = students.OrderBy(s => s.Age);
// 按成绩降序排序
var orderedByScore = students.OrderByDescending(s => s.Score);
GroupBy(分组)
csharp
// 按班级分组
var groups = students.GroupBy(s => s.Class);
// 遍历分组
foreach (var group in groups) {
Console.WriteLine($"Class {group.Key}:");
foreach (var student in group) {
Console.WriteLine(student.Name);
}
}
First / FirstOrDefault(获取首个元素)
csharp
// 获取第一个年龄大于 20 的学生(可能抛异常)
var first = students.First(s => s.Age > 20);
// 安全获取(无匹配返回 null)
var firstOrDefault = students.FirstOrDefault(s => s.Age > 100);
Single / SingleOrDefault(唯一元素)
csharp
// 确保唯一匹配(否则抛异常)
var single = students.Single(s => s.Id == 123);
// 安全获取唯一元素
var singleOrDefault = students.SingleOrDefault(s => s.Id == 456);
Any / All(存在性检查)
csharp
// 是否有学生年龄 > 20
bool hasAdult = students.Any(s => s.Age > 20);
// 是否所有学生成绩 >= 60
bool allPassed = students.All(s => s.Score >= 60);
Count / Sum / Average(聚合)
csharp
// 统计年龄 > 20 的学生数量
int count = students.Count(s => s.Age > 20);
// 计算所有学生的平均分
double avgScore = students.Average(s => s.Score);
// 总成绩
double totalScore = students.Sum(s => s.Score);
Take / Skip(分页)
csharp
// 取前 5 条数据
var page1 = students.Take(5);
// 跳过前 5 条,取接下来的 5 条(分页)
var page2 = students.Skip(5).Take(5);
Join(连接)
csharp
var courses = new List<Course> { /* 课程列表 */ };
// 学生与课程通过 CourseId 连接
var joined = students.Join(
courses,
s => s.CourseId,
c => c.Id,
(s, c) => new { s.Name, CourseName = c.Name }
);
Distinct(去重)
csharp
// 去重后的所有班级名称
var distinctClasses = students.Select(s => s.Class).Distinct();
SelectMany(扁平化嵌套集合)
csharp
var classes = new List<Class> { /* 班级列表(含学生集合) */ };
// 所有班级的所有学生
var allStudents = classes.SelectMany(c => c.Students);
Union / Intersect / Except(集合操作)
csharp
var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 3, 4, 5 };
var union = list1.Union(list2); // {1,2,3,4,5}
var intersect = list1.Intersect(list2); // {3}
var except = list1.Except(list2); // {1,2}
OfType / Cast(类型筛选)
csharp
var mixedList = new ArrayList { 1, "apple", 2, "banana" };
// 提取所有字符串
var strings = mixedList.OfType<string>();
Aggregate(自定义聚合)
csharp
//对集合元素执行累积操作
int[] numbers = { 1, 2, 3, 4 };
// 计算乘积:1*2*3*4
int product = numbers.Aggregate((acc, num) => acc * num);
// 指定初始值(例如累加字符串)
string concatenated = numbers.Aggregate("", (acc, num) => acc + num.ToString());
Max / Min(极值)
csharp
var maxAge = students.Max(s => s.Age);
var minScore = students.Min(s => s.Score);
Last / LastOrDefault(获取最后一个元素)
csharp
// 最后一个年龄大于 20 的学生
var last = students.Last(s => s.Age > 20);
var lastOrDefault = students.LastOrDefault(s => s.Age > 100);
Reverse(反转顺序)
csharp
var reversed = numbers.Reverse(); // {4,3,2,1}
Concat(连接两个集合)
csharp
var list1 = new List<int> { 1, 2 };
var list2 = new List<int> { 3, 4 };
var combined = list1.Concat(list2); // {1,2,3,4}
SequenceEqual(判断集合相等性)
csharp
var listA = new List<int> { 1, 2 };
var listB = new List<int> { 1, 2 };
bool isEqual = listA.SequenceEqual(listB); // true
DefaultIfEmpty(空集合默认值)
csharp
var emptyList = new List<int>();
// 若集合为空,返回包含默认值的单元素集合
var result = emptyList.DefaultIfEmpty(0); // {0}
Zip(合并两个集合)
csharp
var names = new[] { "Alice", "Bob" };
var ages = new[] { 25, 30 };
// 合并为元组:("Alice",25), ("Bob",30)
var zipped = names.Zip(ages, (name, age) => (name, age));
ToDictionary / ToLookup(转换为字典或分组字典)
csharp
// 将学生列表转为以Id为键的字典
var dict = students.ToDictionary(s => s.Id);
// 创建分组查找结构(类似 GroupBy,但立即执行)
var lookup = students.ToLookup(s => s.Class);
TakeWhile / SkipWhile(条件分页)
csharp
int[] nums = { 1, 2, 3, 4, 5, 1 };
// 取元素直到遇到 >=3 的值
var takeWhile = nums.TakeWhile(n => n < 3); // {1,2}
// 跳过元素直到遇到 >=3 的值
var skipWhile = nums.SkipWhile(n => n < 3); // {3,4,5,1}
ElementAt / ElementAtOrDefault(按索引访问)
csharp
var thirdStudent = students.ElementAt(2); // 索引从0开始
var safeElement = students.ElementAtOrDefault(100); // 越界返回 null
Range / Repeat(生成序列)
csharp
// 生成数字序列 10-14
var range = Enumerable.Range(10, 5); // {10,11,12,13,14}
// 生成重复值
var repeats = Enumerable.Repeat("Hello", 3); // {"Hello","Hello","Hello"}
Cast(强制类型转换)
csharp
var mixedList = new ArrayList { 1, 2, 3 };
// 转换为 IEnumerable<int>
var numbers = mixedList.Cast<int>();
DistinctBy / UnionBy(基于属性的去重/合并)
csharp
(需 .NET 6+ 或引入 System.Linq 的扩展包)
// 根据班级去重
var distinctByClass = students.DistinctBy(s => s.Class);
// 合并并去重(根据ID)
var unionBy = list1.UnionBy(list2, x => x.Id);
Chunk(分块)
csharp
//(需 .NET 6+)
int[] numbers = { 1, 2, 3, 4, 5 };
// 每块大小为2
var chunks = numbers.Chunk(2); // { {1,2}, {3,4}, {5} }
AsParallel(并行查询)
csharp
var bigData = Enumerable.Range(1, 1000000);
// 并行处理
var parallelResult = bigData.AsParallel()
.Where(n => n % 2 == 0)
.ToList();
OfType(过滤类型)
csharp
object[] objs = { 1, "apple", 2, "banana" };
// 提取所有整数
var ints = objs.OfType<int>(); // {1,2}
Prepend / Append(添加元素)
csharp
var nums = new[] { 2, 3 };
var newNums = nums.Prepend(1).Append(4); // {1,2,3,4}
LongCount(大集合计数)
csharp
// 返回 long 类型的计数(适用于超大集合)
long count = bigData.LongCount();
TakeLast / SkipLast(获取或跳过末尾元素)
csharp
int[] numbers = { 1, 2, 3, 4, 5 };
var lastTwo = numbers.TakeLast(2); // {4,5}
var skipLastThree = numbers.SkipLast(3); // {1,2}
MaxBy / MinBy(按属性取极值对象)
csharp
//(需 .NET 6+)
// 取分数最高的学生
var topStudent = students.MaxBy(s => s.Score);
// 取年龄最小的学生
var youngest = students.MinBy(s => s.Age);
ToHashSet(快速去重集合)
csharp
//(需 .NET Framework 4.7.2+ 或 .NET Core 2.0+)
var uniqueNumbers = new[] { 1, 2, 2, 3 }.ToHashSet(); // {1,2,3}
ExceptBy / IntersectBy(基于键的集合操作)
csharp
var listA = new[] { new { Id = 1 }, new { Id = 2 } };
var listB = new[] { new { Id = 2 }, new { Id = 3 } };
// 按 Id 排除交集
var except = listA.ExceptBy(listB.Select(x => x.Id), x => x.Id); // {Id=1}
AsEnumerable / AsQueryable(切换执行上下文)
csharp
// 将 IQueryable 转为 IEnumerable(后续操作在内存执行)
var inMemoryQuery = dbContext.Students.AsEnumerable().Where(s => s.Age > 20);
// 将 IEnumerable 转为 IQueryable(用于动态构建数据库查询)
var queryable = students.AsQueryable().Where(s => s.Age > 20);