C# LINQ 快速入门实战指南,建议收藏学习!
笔记哥 /
04-14 /
40点赞 /
0评论 /
557阅读
## 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中常用方法
### 操作示例数据
```csharp
public class StudentInfo
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public DateTime Birthday { get; set; }
public int ClassID { get; set; }
public string Address { get; set; }
public List Courses { get; set; } = new List();
}
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
}
static List students = new List
{
new StudentInfo
{
StudentID=1,
StudentName="大姚",
Birthday=Convert.ToDateTime("1997-10-25"),
ClassID=101,
Courses = new List
{
new Course { CourseID = 101, CourseName = "语文" },
new Course { CourseID = 102, CourseName = "数学" }
}
},
new StudentInfo
{
StudentID=2,
StudentName="李四",
Birthday=Convert.ToDateTime("1998-10-25"),
ClassID=101,
Courses = new List
{
new Course { CourseID = 101, CourseName = "语文" },
new Course { CourseID = 102, CourseName = "数学" }
}
},
new StudentInfo
{
StudentID=3,
StudentName="王五",
Birthday=Convert.ToDateTime("1999-10-25"),
ClassID=102,
Address="广州",
Courses = new List
{
new Course { CourseID = 101, CourseName = "语文" },
new Course { CourseID = 102, CourseName = "数学" }
}
},
new StudentInfo
{
StudentID=4,
StudentName="时光者",
Birthday=Convert.ToDateTime("1999-11-25"),
ClassID=102,
Address="深圳" ,
Courses = new List
{
new Course { CourseID = 104, CourseName = "历史" },
new Course { CourseID = 103, CourseName = "地理" }
}
}
};
```
### 基本查询方法
- **Where**:用于过滤集合中的元素,通过一个谓词(返回布尔值的条件)筛选集合中的元素,生成一个仅包含满足条件元素的新序列。
- **Select**:用于将集合中的每个元素投影(转换)为新序列。
- **SelectMany**:用于将多个集合(嵌套集合,如集合的集合)`展平`为一个集合。
```csharp
var femaleStudents = students.Where(s => s.StudentName == "时光者");
var studentNames = students.Select(s => s.StudentName);
// 使用SelectMany展平所有学生的课程列表
var allCourses = students.SelectMany(student => student.Courses).ToList();
// 输出所有课程的名称
foreach (var course in allCourses)
{
Console.WriteLine(course.CourseName);
}
转换方法
```
### 转换方法
- **ToList**:将实现了`IEnumerable`接口的集合转换为一个`List`类型的对象,属于将集合转换为特定类型列表的方法。
- **ToArray**:将一个实现了`IEnumerable`接口的集合转换为一个数组,属于将集合转换为数组类型的方法。
- **ToDictionary**:将一个`IEnumerable`集合转换为一个`Dictionary`键值对集合(字典)的方法,注意 ToDictionary 要求键唯一,否则抛出异常。
- **ToLookup**:将一个`IEnumerable`集合转换为一个泛型`Lookup`,`Lookup`一个一对多字典,用于将键映射到值的集合。
```csharp
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**:如果集合为空,则返回一个包含默认值的集合。
```csharp
var firstStudent = students.First();
var firstAdult = students.FirstOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18));
var onlyWangWu = students.Single(s => s.StudentName == "王五");
var wangWuOrDefault = students.SingleOrDefault(s => s.StudentName == "王六");
var lastStudent = students.Last();
var lastAdult = students.LastOrDefault(s => s.Birthday <= DateTime.Now.AddYears(-18));
var secondStudent = students.ElementAt(1);
var tenthStudentOrDefault = students.ElementAtOrDefault(9);
var nonEmptyStudents = students.DefaultIfEmpty(new StudentInfo { StudentID = 0, StudentName = "默认Student", Address = "默认" });
```
### 排序方法
- **OrderBy**:用于对集合进行升序排序。
- **OrderByDescending**:用于对集合进行降序排序。
- **ThenBy**:按升序对集合中的元素执行后续排序。
- **ThenByDescending**:按降序对集合中的元素执行后续排序。
```csharp
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**:对集合进行自定义聚合操作。
```csharp
int studentCount = students.Count();
int totalClassID = students.Sum(s => s.ClassID);
double averageAge = students.Average(s => DateTime.Now.Year - s.Birthday.Year);
int minClassID = students.Min(s => s.ClassID);
int maxClassID = students.Max(s => s.ClassID);
string concatenatedNames = students.Aggregate("", (acc, s) => acc == "" ? s.StudentName : acc + ", " + s.StudentName);
```
### 集合操作方法
- **Distinct**:返回集合中的唯一元素(去除重复项)。
- **Union**:返回两个集合的并集(合并后去重)。
- **Intersect**:返回两个集合的交集(共有的唯一元素)。
- **Except**:返回在第一个集合中存在但不在第二个集合中存在的元素(取集合的差集)。
- **Concat**:连接两个集合,返回一个新的序列(保留所有元素,包括重复项)。
```csharp
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**:基于键值等同性将两个集合的元素进行关联,并对结果进行分组。
```csharp
var groupedByClassID = students.GroupBy(s => s.ClassID);
foreach (var group in groupedByClassID)
{
Console.WriteLine($"班级ID: {group.Key}");
foreach (var student in group)
{
Console.WriteLine($" 学生姓名: {student.StudentName}");
}
}
// 连接两个集合(内连接查询)
var otherStudent = new List
{
new StudentInfo
{
StudentID=4,
StudentName="摇一摇",
Birthday=Convert.ToDateTime("1997-10-25"),
ClassID=101,
Courses = new List
{
new Course { CourseID = 101, CourseName = "语文" },
new Course { CourseID = 102, CourseName = "数学" }
}
}
};
var listJoin = students.Join(
otherStudent, // 要连接的第二个集合
s1 => s1.StudentID, // 从第一个集合中提取键
s2 => s2.StudentID, // 从第二个集合中提取键
(s1, s2) => new // 结果选择器,指定如何从两个匹配元素创建结果
{
StudentID = s1.StudentID,
StudentName = s1.StudentName,
Birthday = s1.Birthday,
ClassID = s1.ClassID,
Address = s1.Address,
Courses = s1.Courses,
OtherStudentName = s2.StudentName
});
//使用 GroupJoin 方法实现两个集合的左连接(Left Join)
//目标:获取所有课程及选修学生(即使无人选修也要显示课程)
var courseStudentGroups = courses.GroupJoin(
students.SelectMany(
student => student.Courses,
(student, course) => new { Student = student, Course = course }
),
course => course.CourseID,
studentCoursePair => studentCoursePair.Course.CourseID,
// 结果投影:生成课程名称及对应的学生列表
(course, matchedStudents) => new
{
CourseName = course.CourseName,
Students = matchedStudents
.Select(pair => pair.Student.StudentName)
.DefaultIfEmpty("(无学生)")
.ToList()
}
).ToList();
// 输出结果
foreach (var group in courseStudentGroups)
{
Console.WriteLine("-------------------");
Console.WriteLine($"课程:{group.CourseName}");
Console.WriteLine($"选修学生:{string.Join(", ", group.Students)}");
Console.WriteLine("-------------------");
}
```
### 跳过与获取指定数量的元素(常用作分页)
- **Skip**:用于跳过集合中指定数量的元素,并返回剩余的元素序列。
- **Take**:用于从集合的开头获取指定数量的元素,并返回一个新的序列。
```csharp
var skippedStudents = students.Skip(1);
var takenStudents = students.Take(2);
//数据分页查询(Skip + Take)
int pageNumber = 2;
int pageSize = 10;
var pagedUsers = skippedStudents
.OrderBy(u => u.ClassID) // 必须排序
.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
```
### 条件判断方法
- **All**:判断集合中的所有元素是否都满足条件。
- **Any**:判断集合中是否包含元素或存在元素满足指定条件。
- **Contains**:用于判断集合中是否包含指定的元素。
```csharp
bool allAdults = students.All(s => s.Birthday <= DateTime.Now.AddYears(-18));
bool anyAdults = students.Any(s => s.Birthday <= DateTime.Now.AddYears(-18));
bool containsWangWu = students.Contains(students.First(s => s.StudentName == "王五"));
```
### 更多方法查询
- https://learn.microsoft.com/zh-cn/dotnet/csharp/linq/get-started/introduction-to-linq-queries#classification-table

## 查询语法
LINQ提供了类似于SQL的查询语法,允许开发者以几乎相同的方式对不同类型的数据源进行查询。查询语法使用from、where、select、orderby等关键字。
```csharp
var querySyntaxResult = from student in students
where student.ClassID == 101
orderby student.StudentName ascending
select student;
Console.WriteLine("查询语法结果:");
foreach (var student in querySyntaxResult)
{
Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
}
```
**查询关键字:**
- **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子句中的上下文关键字。
## 方法语法
方法语法也称为扩展方法语法,使用点号“.”和一系列扩展方法来构建查询。
```csharp
var methodSyntaxResult = students
.Where(student => student.ClassID == 101)
.OrderBy(student => student.StudentName)
.ToList();
Console.WriteLine("方法语法结果:");
foreach (var student in methodSyntaxResult)
{
Console.WriteLine($"{student.StudentName}, ClassID: {student.ClassID}");
}
```
## 混合查询和方法语法
```csharp
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
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/2219
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利
- 相关联分享
- .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- .NET Core中的配置Configuration实战
- 在 .NET 中使用 Sqids 快速的为数字 ID 披上神秘短串,轻松隐藏敏感数字!
- 常用的 Visual Studio 2022 扩展插件推荐:生产力必备工具
- 解锁.NET 9性能优化:内存、异步、代码与Web全方位指南
- 一款 .NET 开源、免费、轻量级且非侵入性的防火墙软件
- .NET 10 进展之 CoreCLR Interpreter
- 一款基于 .NET 开源、可以拦截并修改 WinSock 封包的 Windows 软件
- 使用MCP C# SDK开发MCP Server + Client
- Gradio.Net:加速 .NET 的 Web 应用开发
- Magick.NET 支持100多种格式的强大 .NET 图片处理库
- 2025年C#/.NET/.NET Core优秀项目和框架推荐
- EF Core 10 中 LeftJoin 和 RightJoin 运算符在 LINQ 查询中的应用
- [开源][.Net Framework 4.0] SimpleLiveDataFeed v1.0更新:增加NuGet包
- .NET 10 Preview 4中ASP.NET Core 改进
- 通过 Nuke 为 Dotnet Core 应用构建自动化流程
- ASP.NET Core 实现的领域驱动设计框架推荐
- 如何在 .NET 中 使用 ANTLR4
- 如何把ASP.NET Core WebApi打造成Mcp Server
- .NET 开源工业视觉系统 OpenIVS 快速搭建自动化检测平台
- C#/.NET/.NET Core技术前沿周刊 | 第 39 期(2025年5.19-5.25)
- C# LINQ 快速入门实战指南,建议收藏学习!
- 解决.NET AOT交叉编译到Linux - arm64的坑
- 10年+.NET Coder 心语 ── 单一职责原则的思维:为什么你的代码总在"牵一发而动全身"
- ASP.NET Core EFCore 属性配置与DbContext 详解
- 3款基于.NET开源且免费的远程桌面工具分享
- 深入理解.NET Core中的配置Configuration和应用
- .NET 的全新低延时高吞吐自适应 GC - Satori GC