什么是LINQ
LINQ的两种语法
核心LINQ操作符
LINQ to Objects 实战
LINQ to Entities(数据库查询)
延迟执行与立即执行
LINQ 操作符分类速查表
LINQ(Language Integrated Query,语言集成查询)是C# 3.0引入的革命性特性,允许你用统一的语法查询各种数据源(数组、集合、数据库、XML等)。
// 获取所有年龄大于18岁的用户,按姓名排序
List<User> users = GetUsers();
List<User> result = new List<User>();
// 筛选
foreach (var user in users)
{
if (user.Age > 18)
{
result.Add(user);
}
}
// 排序
result.Sort((x, y) => x.Name.CompareTo(y.Name));
// 提取姓名
List<string> names = new List<string>();
foreach (var user in result)
{
names.Add(user.Name);
}
// 代码啰嗦,可读性差!// 链式方法语法(流畅、直观)
var names = users
.Where(u => u.Age > 18)
.OrderBy(u => u.Name)
.Select(u => u.Name)
.ToList();
// 或查询语法(类似SQL)
var names = from u in users
where u.Age > 18
orderby u.Name
select u.Name;// 基本结构
var result = from 变量 in 数据源
where 条件
orderby 排序字段
select 结果;
// 示例
int[] numbers = { 5, 2, 8, 1, 9, 3, 7, 4, 6 };
var evenNumbers = from n in numbers
where n % 2 == 0
orderby n descending
select n;
foreach (var n in evenNumbers)
Console.Write(n + " "); // 8 6 4 2// 基本结构
var result = 数据源
.Where(条件)
.OrderBy(排序字段)
.Select(结果);
// 示例
int[] numbers = { 5, 2, 8, 1, 9, 3, 7, 4, 6 };
var evenNumbers = numbers
.Where(n => n % 2 == 0)
.OrderByDescending(n => n)
.Select(n => n);
foreach (var n in evenNumbers)
Console.Write(n + " "); // 8 6 4 2场景 | 推荐语法 |
|---|---|
简单查询(Where、Select) | 方法语法 |
复杂查询(多表Join、Group) | 查询语法 |
团队习惯 | 统一风格 |
// 复杂查询:查询语法更清晰
var result = from user in users
join order in orders on user.Id equals order.UserId
group order by user.Name into userOrders
select new
{
UserName = userOrders.Key,
TotalAmount = userOrders.Sum(o => o.Amount)
};
// 简单查询:方法语法更简洁
var names = users.Where(u => u.Age > 18).Select(u => u.Name);// Where:条件筛选
var adults = users.Where(u => u.Age >= 18);
// OfType:筛选特定类型
object[] mixed = { 1, "hello", 3.14, "world", 5 };
var strings = mixed.OfType<string>(); // "hello", "world"
// 多个条件
var result = users.Where(u => u.Age > 18 && u.City == "北京");// Select:转换每个元素
var names = users.Select(u => u.Name);
var nameAges = users.Select(u => new { u.Name, u.Age });
// SelectMany:扁平化嵌套集合
// 场景:每个用户有多个订单
var allOrders = users.SelectMany(u => u.Orders);// OrderBy:升序排序
var byName = users.OrderBy(u => u.Name);
// OrderByDescending:降序排序
var byAgeDesc = users.OrderByDescending(u => u.Age);
// ThenBy:次要排序
var sorted = users
.OrderBy(u => u.City)
.ThenBy(u => u.Name);
// ThenByDescending:次要降序排序
var sortedDesc = users
.OrderBy(u => u.City)
.ThenByDescending(u => u.Age);
// Reverse:反转顺序
var reversed = numbers.Reverse();// GroupBy:分组
var groupedByCity = users.GroupBy(u => u.City);
foreach (var group in groupedByCity)
{
Console.WriteLine($"城市:{group.Key}");
foreach (var user in group)
{
Console.WriteLine($" {user.Name}");
}
}
// 分组聚合
var cityStats = users
.GroupBy(u => u.City)
.Select(g => new
{
City = g.Key,
Count = g.Count(),
AvgAge = g.Average(u => u.Age),
MaxAge = g.Max(u => u.Age)
});int[] numbers = { 1, 2, 3, 4, 5 };
int count = numbers.Count();
int sum = numbers.Sum();
int min = numbers.Min();
int max = numbers.Max();
double avg = numbers.Average();
// 自定义聚合:Aggregate
int product = numbers.Aggregate((acc, n) => acc * n); // 1*2*3*4*5 = 120
// 字符串拼接
string[] words = { "Hello", "World", "LINQ" };
string sentence = words.Aggregate((acc, w) => acc + " " + w);
// "Hello World LINQ"int[] numbers1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 3, 4, 5, 6, 7 };
// 交集:{3,4,5}
var intersect = numbers1.Intersect(numbers2);
// 并集:{1,2,3,4,5,6,7}
var union = numbers1.Union(numbers2);
// 差集:{1,2}(在numbers1中但不在numbers2中)
var except = numbers1.Except(numbers2);
// 去重
int[] withDuplicates = { 1, 1, 2, 2, 3, 3 };
var distinct = withDuplicates.Distinct(); // {1,2,3}int[] numbers = { 1, 2, 3, 4, 5 };
// Any:是否存在至少一个满足条件
bool hasEven = numbers.Any(n => n % 2 == 0); // true
// All:是否所有元素都满足条件
bool allPositive = numbers.All(n => n > 0); // true
// Contains:是否包含特定元素
bool hasThree = numbers.Contains(3); // true
// 空集合判断
bool isEmpty = !numbers.Any(); // 或 numbers.Count() == 0// Skip:跳过前N个
// Take:取前N个
var page1 = users.Skip(0).Take(10); // 第1页(10条)
var page2 = users.Skip(10).Take(10); // 第2页
var page3 = users.Skip(20).Take(10); // 第3页
// 跳过满足条件的元素,直到不满足条件为止
var result = numbers.SkipWhile(n => n < 5);
// 如果 numbers = {1,3,5,7,9},结果是 {5,7,9}
// 取满足条件的元素,直到不满足条件为止
var result2 = numbers.TakeWhile(n => n < 5);
// 如果 numbers = {1,3,5,7,9},结果是 {1,3}int[] numbers = { 1, 2, 3, 4, 5 };
// First:第一个元素(不存在的抛出异常)
int first = numbers.First(); // 1
int firstEven = numbers.First(n => n % 2 == 0); // 2
// FirstOrDefault:第一个元素(不存在返回默认值)
int firstDefault = numbers.FirstOrDefault(n => n > 10); // 0
// Last:最后一个元素
int last = numbers.Last(); // 5
// Single:唯一的元素(不唯一或不存在抛出异常)
int[] single = { 42 };
int only = single.Single(); // 42
// SingleOrDefault:唯一的元素(不存在返回默认值)
int onlyDefault = single.SingleOrDefault(); // 42
// ElementAt:指定索引的元素
int element = numbers.ElementAt(2); // 3// 内连接(Inner Join)
var query = from user in users
join order in orders on user.Id equals order.UserId
select new
{
UserName = user.Name,
OrderId = order.Id,
OrderAmount = order.Amount
};
// 方法语法
var query2 = users.Join(
orders,
user => user.Id,
order => order.UserId,
(user, order) => new
{
UserName = user.Name,
OrderId = order.Id,
OrderAmount = order.Amount
}
);
// 分组连接(Group Join)
var groupJoin = from user in users
join order in orders on user.Id equals order.UserId into userOrders
select new
{
UserName = user.Name,
OrderCount = userOrders.Count(),
TotalAmount = userOrders.Sum(o => o.Amount)
};
// 左外连接(Left Outer Join)
var leftJoin = from user in users
join order in orders on user.Id equals order.UserId into userOrders
from order in userOrders.DefaultIfEmpty()
select new
{
UserName = user.Name,
OrderAmount = order?.Amount ?? 0
};using System;
using System.Collections.Generic;
using System.Linq;
class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Position { get; set; }
public decimal Salary { get; set; }
public DateTime HireDate { get; set; }
public bool IsActive { get; set; }
public List<Project> Projects { get; set; } = new();
}
class Project
{
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public string Status { get; set; }
}
class Program
{
static void Main()
{
// 准备数据
var employees = GetEmployees();
var projects = GetProjects();
// 1. 查询所有活跃的研发部员工,按工资降序
var activeDevs = employees
.Where(e => e.IsActive && e.Department == "研发部")
.OrderByDescending(e => e.Salary)
.Select(e => new { e.Name, e.Position, e.Salary });
Console.WriteLine("=== 活跃研发部员工(按工资降序)===");
foreach (var emp in activeDevs)
{
Console.WriteLine($" {emp.Name} - {emp.Position}: {emp.Salary:C}");
}
// 2. 按部门统计
var deptStats = employees
.Where(e => e.IsActive)
.GroupBy(e => e.Department)
.Select(g => new
{
Department = g.Key,
EmployeeCount = g.Count(),
AverageSalary = g.Average(e => e.Salary),
MaxSalary = g.Max(e => e.Salary),
MinSalary = g.Min(e => e.Salary)
})
.OrderByDescending(s => s.AverageSalary);
Console.WriteLine("\n=== 部门统计 ===");
foreach (var stats in deptStats)
{
Console.WriteLine($" {stats.Department}:");
Console.WriteLine($" 人数: {stats.EmployeeCount}");
Console.WriteLine($" 平均工资: {stats.AverageSalary:C}");
Console.WriteLine($" 最高工资: {stats.MaxSalary:C}");
Console.WriteLine($" 最低工资: {stats.MinSalary:C}");
}
// 3. 工龄分析
var today = DateTime.Today;
var seniority = employees
.Where(e => e.IsActive)
.Select(e => new
{
e.Name,
YearsOfWork = (today - e.HireDate).Days / 365,
e.Salary
})
.OrderByDescending(e => e.YearsOfWork);
Console.WriteLine("\n=== 工龄分析 ===");
foreach (var emp in seniority.Take(5))
{
Console.WriteLine($" {emp.Name}: {emp.YearsOfWork}年, 工资: {emp.Salary:C}");
}
// 4. 查询参与多个项目的员工
var multiProjectEmployees = employees
.Where(e => e.Projects.Count > 1)
.Select(e => new
{
e.Name,
ProjectCount = e.Projects.Count,
Projects = string.Join(", ", e.Projects.Select(p => p.Name))
});
Console.WriteLine("\n=== 参与多个项目的员工 ===");
foreach (var emp in multiProjectEmployees)
{
Console.WriteLine($" {emp.Name} ({emp.ProjectCount}个项目)");
Console.WriteLine($" 项目:{emp.Projects}");
}
// 5. 复杂查询:三表连接
var complexQuery = from emp in employees
join proj in projects on emp.Department equals proj.Department into deptProjects
from project in deptProjects.DefaultIfEmpty()
where emp.IsActive
select new
{
emp.Name,
emp.Department,
Project = project?.Name ?? "无项目",
ProjectStatus = project?.Status ?? "N/A"
};
Console.WriteLine("\n=== 员工与项目关联 ===");
foreach (var item in complexQuery.Take(10))
{
Console.WriteLine($" {item.Name} ({item.Department}) -> {item.Project} [{item.ProjectStatus}]");
}
// 6. 分页
int pageSize = 3;
int pageNumber = 2;
var pagedEmployees = employees
.Where(e => e.IsActive)
.OrderBy(e => e.Name)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.Select(e => e.Name);
Console.WriteLine($"\n=== 第{pageNumber}页员工(每页{pageSize}条)===");
foreach (var name in pagedEmployees)
{
Console.WriteLine($" {name}");
}
}
static List<Employee> GetEmployees()
{
return new List<Employee>
{
new Employee
{
Id = 1,
Name = "张三",
Department = "研发部",
Position = "高级工程师",
Salary = 15000,
HireDate = new DateTime(2018, 3, 1),
IsActive = true,
Projects = new List<Project>
{
new Project { Name = "项目A", Status = "进行中" },
new Project { Name = "项目B", Status = "已完成" }
}
},
new Employee
{
Id = 2,
Name = "李四",
Department = "研发部",
Position = "工程师",
Salary = 12000,
HireDate = new DateTime(2019, 6, 15),
IsActive = true,
Projects = new List<Project>
{
new Project { Name = "项目A", Status = "进行中" }
}
},
new Employee
{
Id = 3,
Name = "王五",
Department = "市场部",
Position = "经理",
Salary = 20000,
HireDate = new DateTime(2015, 9, 1),
IsActive = true,
Projects = new List<Project>()
},
new Employee
{
Id = 4,
Name = "赵六",
Department = "财务部",
Position = "主管",
Salary = 18000,
HireDate = new DateTime(2017, 11, 20),
IsActive = false,
Projects = new List<Project>()
},
new Employee
{
Id = 5,
Name = "孙七",
Department = "研发部",
Position = "架构师",
Salary = 25000,
HireDate = new DateTime(2014, 2, 10),
IsActive = true,
Projects = new List<Project>
{
new Project { Name = "项目C", Status = "规划中" }
}
}
};
}
static List<Project> GetProjects()
{
return new List<Project>
{
new Project { Name = "项目A", Department = "研发部", Status = "进行中" },
new Project { Name = "项目B", Department = "研发部", Status = "已完成" },
new Project { Name = "项目C", Department = "市场部", Status = "规划中" }
};
}
}
class Project
{
public string Name { get; set; }
public string Department { get; set; }
public string Status { get; set; }
}// Entity Framework Core 中使用LINQ
using (var context = new AppDbContext())
{
// 查询语法(翻译成SQL)
var query = from user in context.Users
where user.Age > 18
select user;
// 方法语法
var users = context.Users
.Where(u => u.Age > 18)
.OrderBy(u => u.Name)
.Select(u => new { u.Name, u.Email })
.ToList(); // 执行SQL查询
// 延迟执行
var query2 = context.Orders
.Where(o => o.Amount > 100)
.OrderByDescending(o => o.CreatedAt);
// 此时还没执行SQL
var result = query2.ToList(); // 执行SQL
// 立即执行(强制立即执行)
var count = context.Users.Count(); // SELECT COUNT(*)...
var exists = context.Users.Any(u => u.Name == "张三"); // SELECT EXISTS...
var first = context.Users.First(); // SELECT TOP 1...
}// 延迟执行:查询在枚举时才执行
var query = numbers.Where(n => n > 3); // 此时不执行
Console.WriteLine("查询已定义");
foreach (var n in query) // 此时才执行查询
{
Console.WriteLine(n);
}
// 延迟执行的优点:数据源变化时,查询结果会更新
var numbers = new List<int> { 1, 2, 3 };
var query = numbers.Select(n => n * 2);
numbers.Add(4); // 修改数据源
foreach (var n in query) // 输出 2,4,6,8(包含新增的数据)
{
Console.WriteLine(n);
}// 立即执行:调用这些方法时立即执行查询
var count = numbers.Count(); // 立即执行
var sum = numbers.Sum(); // 立即执行
var list = numbers.Where(n => n > 3).ToList(); // 立即执行
var array = numbers.Where(n => n > 3).ToArray(); // 立即执行
// 数据源变化不影响已执行的结果
var numbers = new List<int> { 1, 2, 3 };
var list = numbers.Where(n => n > 1).ToList(); // {2,3}
numbers.Add(4); // 修改数据源
// list 仍然是 {2,3},不会包含 4方法 | 说明 | 示例 |
|---|---|---|
| 条件筛选 |
|
| 按类型筛选 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 转换元素 |
|
| 扁平化 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 升序排序 |
|
| 降序排序 |
|
| 次要升序 |
|
| 次要降序 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 分组 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 计数 |
|
| 求和 |
|
| 最小值 |
|
| 最大值 |
|
| 平均值 |
|
| 自定义聚合 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 去重 |
|
| 并集 |
|
| 交集 |
|
| 差集 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 第一个 |
|
| 第一个或默认 |
|
| 最后一个 |
|
| 唯一元素 |
|
| 唯一或默认 |
|
| 指定索引 |
|
| 空集合默认值 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 是否存在 |
|
| 是否全部满足 |
|
| 是否包含 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 跳过N个 |
|
| 取N个 |
|
| 跳过满足条件的 |
|
| 取满足条件的 |
|
方法 | 说明 | 示例 |
|---|---|---|
| 内连接 |
|
| 分组连接 |
|