介绍
Linq(Language Integrated Query),语言集成查询,是一种使用 类似SQL语句 操作多种数据源的功能。
从.net framework3.5中开始引入,能够提升程序数据处理能力和开发效率,具有集成性、统一性、可扩展性、抽象性、说明式编程、可组成型、可转换性等优势。
支持的数据源
包括但不限于:
- 内存中的集合(如
List,Array,SortedSet,Stack,Queue等) - 数据库(如SQL Server)
- XML文档
- Web服务
类比 Java 的 stream
类似 Java 的 stream,但功能更强大
实现方式
有两种:
方法查询。类似 Java 的 stream,通过集合的 扩展方法 和 lambda表达式 实现
查询表达式语法,类似 SQL 语句(某些功能不支持,需要方法查询)
原理: 标准查询运算符是一组 扩展方法,它们提供了一种 函数式 的方式来构建查询。这些方法定义在 System.Linq.Enumerable 类中,适用于所有实现了 IEnumerable<T> 接口的集合。
前提条件
使用 LINQ 方法语法需满足:
- 引用
System.Linq命名空间(默认在 C# 项目中已包含)。 - 数据源需实现
IEnumerable<T>或IQueryable<T>接口(如List<T>、Array、数据库查询结果等)。
方法查询
方法语法(也称为流利语法)主要利用 System.Linq.Enumerable 类中定义的扩展方法和 Lambda表达式 方式进行查询,类似于如何调用任何类的扩展方法
例子
以下是一个示例LINQ方法语法的查询,返回数组中的偶数:
var nums = new int[] { 56, 97, 98, 57, 74, 86, 31, 90 };
var result = nums.Where(p => p % 2 == 0).ToArray();
从上面的示例代码中可以看出:方法语法包括扩展方法和Lambda表达式。 扩展方法 Where()在Enumerable类中定义。
准备数据
using System;
using System.Collections.Generic;
using System.Linq;
// 定义实体类
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Major { get; set; }
public double Score { get; set; }
}
// 数据源:学生列表
List<Student> students = new List<Student>
{
new Student { Id = 1, Name = "张三", Age = 20, Major = "计算机", Score = 85.5 },
new Student { Id = 2, Name = "李四", Age = 22, Major = "数学", Score = 92.0 },
new Student { Id = 3, Name = "王五", Age = 19, Major = "计算机", Score = 78.0 },
new Student { Id = 4, Name = "赵六", Age = 21, Major = "数学", Score = 88.5 },
new Student { Id = 5, Name = "钱七", Age = 20, Major = "计算机", Score = 90.0 }
};
过滤 Where
作用:筛选出符合条件的元素,返回 IEnumerable<T>。
参数:Lambda 表达式(Func<TSource, bool>),定义筛选条件。
// 示例:查询“计算机”专业的学生
var csStudents = students.Where(s => s.Major == "计算机");
// 遍历结果
foreach (var s in csStudents)
{
Console.WriteLine($"{s.Id}: {s.Name} ({s.Major})");
}
// 输出:
// 1: 张三 (计算机)
// 3: 王五 (计算机)
// 5: 钱七 (计算机)
投影 Select
作用:将元素转换为新的形式(如提取部分字段、转换类型),返回 IEnumerable<TResult>。
参数:Lambda 表达式(Func<TSource, TResult>),定义转换规则。
// 示例:提取学生的“姓名”和“分数”,转换为匿名类型
var nameAndScores = students.Select(s => new { s.Name, s.Score });
foreach (var item in nameAndScores)
{
Console.WriteLine($"{item.Name}: {item.Score}");
}
// 输出:
// 张三: 85.5
// 李四: 92.0
// ...(省略其他)
排序(OrderBy / OrderByDescending)
作用:对元素按指定字段排序,OrderBy 升序,OrderByDescending 降序。
参数:Lambda 表达式(Func<TSource, TKey>),指定排序字段。
// 示例:按分数降序排列所有学生
var sortedByScore = students.OrderByDescending(s => s.Score);
foreach (var s in sortedByScore)
{
Console.WriteLine($"{s.Name}: {s.Score}");
}
// 输出:
// 李四: 92.0
// 钱七: 90.0
// 张三: 85.5
// 赵六: 88.5(注意:88.5 < 90.0,所以在钱七之后)
// 王五: 78.0
限制结果(Take / Skip)
Take(n):获取前n个元素。Skip(n):跳过前n个元素,获取剩余元素。
// 示例:取分数最高的前2名学生
var top2 = students.OrderByDescending(s => s.Score).Take(2);
foreach (var s in top2)
{
Console.WriteLine($"{s.Name}: {s.Score}");
}
// 输出:
// 李四: 92.0
// 钱七: 90.0
聚合(Count / Sum / Average / Max / Min)
作用:对集合进行统计计算(立即执行,非延迟执行)。
// 示例:统计计算机专业学生的数量、平均年龄、最高分数
int csCount = students.Where(s => s.Major == "计算机").Count();
double avgAge = students.Where(s => s.Major == "计算机").Average(s => s.Age);
double maxScore = students.Where(s => s.Major == "计算机").Max(s => s.Score);
Console.WriteLine($"计算机专业人数:{csCount},平均年龄:{avgAge},最高分数:{maxScore}");
// 输出:计算机专业人数:3,平均年龄:19.666...,最高分数:90.0
分组(GroupBy)
作用:按指定字段将元素分组,返回 IEnumerable<IGrouping<TKey, TSource>>。
// 示例:按专业分组,获取每个专业的学生
var groupByMajor = students.GroupBy(s => s.Major);
foreach (var group in groupByMajor)
{
Console.WriteLine($"专业:{group.Key},人数:{group.Count()}");
foreach (var s in group)
{
Console.WriteLine($" {s.Name}");
}
}
// 输出:
// 专业:计算机,人数:3
// 张三
// 王五
// 钱七
// 专业:数学,人数:2
// 李四
// 赵六
连接(Join)
作用:类似 SQL 的 JOIN,根据两个集合的共同字段关联数据。
假设有第二个数据源(课程):
List<Course> courses = new List<Course>
{
new Course { CourseId = 101, CourseName = "C#编程", Major = "计算机" },
new Course { CourseId = 102, CourseName = "高等数学", Major = "数学" }
};
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public string Major { get; set; }
}
// 示例:关联学生和课程(按专业),获取“学生姓名-专业-课程名”
var studentCourses = students.Join(
courses,
s => s.Major, // 学生集合的关联字段
c => c.Major, // 课程集合的关联字段
(s, c) => new { // 关联后的结果
StudentName = s.Name,
Major = s.Major,
CourseName = c.CourseName
}
);
foreach (var item in studentCourses)
{
Console.WriteLine($"{item.StudentName} - {item.Major} - {item.CourseName}");
}
// 输出:
// 张三 - 计算机 - C#编程
// 王五 - 计算机 - C#编程
// 钱七 - 计算机 - C#编程
// 李四 - 数学 - 高等数学
// 赵六 - 数学 - 高等数学
方法链(Method Chaining)
LINQ 方法语法支持链式调用,将多个方法组合成一个查询,可读性和灵活性更高。
// 示例:查询计算机专业中年龄>19、分数>80的学生,按分数升序,取前2名,只显示姓名和分数
var result = students
.Where(s => s.Major == "计算机" && s.Age > 19 && s.Score > 80) // 过滤
.OrderBy(s => s.Score) // 排序
.Take(2) // 限制数量
.Select(s => new { s.Name, s.Score }); // 投影
foreach (var item in result)
{
Console.WriteLine($"{item.Name}: {item.Score}");
}
// 输出:
// 张三: 85.5
// 钱七: 90.0
延迟执行 vs 立即执行
延迟执行:
Where、Select、OrderBy、GroupBy等方法不会立即执行,仅定义查询规则,直到枚举结果时才执行(如foreach、ToList())。
优点:可组合多个查询步骤,最终一次性执行,提高效率。立即执行:
Count()、Sum()、ToList()、ToArray()等方法会立即执行查询并返回结果。
// 延迟执行:此时未执行查询
var query = students.Where(s => s.Age > 20);
// 立即执行:调用 ToList() 时执行查询
var list = query.ToList();
总结
LINQ 方法语法通过扩展方法和 Lambda 表达式,提供了简洁、灵活的查询能力,核心优势:
- 统一查询语法:对各种数据源(集合、数据库、XML 等)使用相同的查询方式。
- 类型安全:编译时检查查询语法和类型,减少运行时错误。
- 可组合性:通过方法链组合多个操作,实现复杂查询。
掌握常用方法(Where、Select、OrderBy、GroupBy 等)及其组合使用,是高效开发 .NET 应用的基础。
查询表达式语法
类似 SQL 语句(某些功能不支持,需要方法查询)
常见的标准查询运算符包括:
- Where:过滤集合中的元素。
- Select:投影每个元素。
- OrderBy 和 OrderByDescending:按升序或降序排序。
- GroupBy:根据键值对元素进行分组。
- Join:将两个集合基于键值进行关联。
- Any 和 All:检查集合是否满足某些条件。
- Count 和 Sum:计算集合的大小或求和。
整体语法
from source in collection:指定查询的数据源。
where condition:指定筛选条件。
orderby key [descending]:指定排序条件。
select result:定义查询结果,一般以 select 结尾
基本查询
语法
from item in collection:指定查询的数据源
select item:定义查询结果
理解: from item in collection 类似 foreach( var item in collection )
例子
var nums = new int[] { 56, 97, 98, 57, 74, 86, 31, 90 };
//num为数据中的每个元素(相当于foreach中迭代变量)
var queryInfo = from num in nums select num;
foreach (var item in queryInfo)
{
System.Diagnostics.Debug.WriteLine(item);
}
执行结果:
97
98
74
86
90
where条件查询
where子句用来指定筛选的条件
语法
from item in collection:指定查询的数据源。
where condition:指定筛选条件。
select item:定义查询结果
例子
var nums = new int[] { 56, 97, 98, 57, 74, 86, 31, 90 };
//num为数据中的每个元素(相当于foreach中迭代变量)
var queryInfo = from num in nums where num >60 select num;
foreach (var item in queryInfo)
{
System.Diagnostics.Debug.WriteLine(item);
}
执行结果:
97
98
74
86
90
select查询列
select子句,基于查询结果返回需要的 字段,并能够对返回值指定类型。对任意想要获取到返回结果的linq语句,必须以 select 或 group 结束。
准备工作:
class Person
{
public string Name { set; get; }
public int Age { set; get; }
public string Gender { set; get; }
public override string ToString() => Name;
}
初始化 List:
static List<Person> init()
{
List<Person> personList = new List<Person>
{
new Person
{
Name = "李雷", Age = 18, Gender = "Male"
},
new Person
{
Name = "张三", Age = 19, Gender = "Male",
},
new Person
{
Name = "韩梅梅", Age = 17,Gender = "Female",
}
};
return personList;
}
查询一列
List<Person> list = init();
// 当 item 是对象时,可以只查询某个属性
var queryInfo = from item in list select item.Name;
foreach (var item in queryInfo)
{
Console.WriteLine(item);
}
执行结果:
李雷
张三
韩梅梅
查询整个对象
List<Person> list = init();
// 当 item 是对象时,也可以查询 item
var queryInfo = from item in list select item;
foreach (var item in queryInfo)
{
Console.WriteLine(item.Name+","+item.Age);
}
执行结果:
李雷,18
张三,19
韩梅梅,17
查询多列
List<Person> list = init();
// Select 后面跟匿名类,封装查询的字段
var queryInfo = from item in list select new { username = item.Name, uesrAge = item.Age };
foreach (var item in queryInfo)
{
Console.WriteLine(item.username + ","+item.uesrAge);
}
执行结果:
李雷,18
张三,19
韩梅梅,17
order by
linq中语句默认展示为升序,降序使用 descending
语法
from item in collection:指定查询的数据源。
where condition:指定筛选条件。
orderby key [descending]:指定排序条件。
select 字段:定义查询结果,一般以 select 结尾
例子-升序
var scores = new int[] { 56, 97, 98, 57, 74, 86, 31, 90 };
var query = from item in scores orderby item select item;//升序
foreach (var item in query)
{
Console.WriteLine(item);
}
例子-降序
//var orderQuery = from item in scores orderby item descending select item;//降序
group by
group by 子句用来对查询结果进行分组
- 如果结果分为两组,且 未指定key 的情况下,
key取值默认是true和false。 - 如果分为多组,获取数据结果时需要手动遍历
key获取对应的value
语法
from item in collection:指定查询的数据源。
group item by item.属性
例子
List<Person> list = init();
// Select 后面跟匿名类,封装查询的字段
var queryInfo = from item in list
group item by item.Gender;
foreach (var item in queryInfo)
{
Console.WriteLine(item.Key);
}
group by 聚合查询
例子
List<Person> list = init();
// Select 后面跟匿名类,封装查询的字段
var queryInfo = from item in list
group item by item.Gender into g
select new {
Key = g.Key,
count = g.Count(),
maxAge = g.Max( x=> x.Age),
minAge = g.Min(x => x.Age),
sumAge = g.Sum(x => x.Age),
avgAge = g.Average(s => s.Age)
};
foreach (var item in queryInfo)
{
Console.WriteLine($"{item.Key}---总行数:{item.count},最大年龄:{item.maxAge},最小年龄:{item.minAge},年龄和:{item.sumAge},平均年龄:{item.avgAge}");
}
执行结果:
Male---总行数:2,最大年龄:19,最小年龄:18,年龄和:37,平均年龄:18.5
Female---总行数:1,最大年龄:17,最小年龄:17,年龄和:17,平均年龄:17
参考:
https://blog.csdn.net/m0_56259289/article/details/144134122
https://blog.csdn.net/qq_34550459/article/details/113540805
https://zhuanlan.zhihu.com/p/146747701