C# 学习笔记五:LINQ (Language Integrated Query)

老牛浏览 44评论 0发表于

目录

  1. 什么是LINQ

  2. LINQ的两种语法

  3. 核心LINQ操作符

  4. LINQ to Objects 实战

  5. LINQ to Entities(数据库查询)

  6. 延迟执行与立即执行

  7. LINQ 操作符分类速查表

第一部分:什么是LINQ?

LINQ(Language Integrated Query,语言集成查询)是C# 3.0引入的革命性特性,允许你用统一的语法查询各种数据源(数组、集合、数据库、XML等)。

没有LINQ之前(传统方式)

csharp
// 获取所有年龄大于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);
}
// 代码啰嗦,可读性差!

使用LINQ之后

csharp
// 链式方法语法(流畅、直观)
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;

第二部分:LINQ的两种语法

1. 查询语法(Query Syntax)——类似SQL

csharp
// 基本结构
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

2. 方法语法(Method Syntax)——链式调用

csharp
// 基本结构
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

3. 两种语法的选择

场景

推荐语法

简单查询(Where、Select)

方法语法

复杂查询(多表Join、Group)

查询语法

团队习惯

统一风格

csharp
// 复杂查询:查询语法更清晰
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);

第三部分:核心LINQ操作符

1. 筛选操作符(Filtering)

csharp
// 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 == "北京");

2. 投影操作符(Projection)

csharp
// 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);

3. 排序操作符(Ordering)

csharp
// 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();

4. 分组操作符(Grouping)

csharp
// 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)
    });

5. 聚合操作符(Aggregation)

csharp
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"

6. 集合操作符(Set Operations)

csharp
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}

7. 量词操作符(Quantifiers)

csharp
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

8. 分页操作符(Paging)

csharp
// 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}

9. 元素操作符(Element Operations)

csharp
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

10. 连接操作符(Join)

csharp
// 内连接(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
               };

第四部分:LINQ to Objects 实战

实战案例:员工管理系统

csharp
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; }
}

第五部分:LINQ to Entities(数据库查询)

csharp
// 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...
}

第六部分:延迟执行与立即执行

延迟执行(Deferred Execution)

csharp
// 延迟执行:查询在枚举时才执行
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);
}

立即执行(Immediate Execution)

csharp
// 立即执行:调用这些方法时立即执行查询
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

第七部分:LINQ 操作符分类速查表

筛选

方法

说明

示例

Where

条件筛选

Where(x => x > 5)

OfType

按类型筛选

OfType<string>()

投影

方法

说明

示例

Select

转换元素

Select(x => x * 2)

SelectMany

扁平化

SelectMany(x => x.Items)

排序

方法

说明

示例

OrderBy

升序排序

OrderBy(x => x.Name)

OrderByDescending

降序排序

OrderByDescending(x => x.Age)

ThenBy

次要升序

ThenBy(x => x.Id)

ThenByDescending

次要降序

ThenByDescending(x => x.Id)

分组

方法

说明

示例

GroupBy

分组

GroupBy(x => x.City)

聚合

方法

说明

示例

Count

计数

Count()

Sum

求和

Sum(x => x.Amount)

Min

最小值

Min(x => x.Age)

Max

最大值

Max(x => x.Salary)

Average

平均值

Average(x => x.Score)

Aggregate

自定义聚合

Aggregate((a,b) => a + b)

集合

方法

说明

示例

Distinct

去重

Distinct()

Union

并集

Union(other)

Intersect

交集

Intersect(other)

Except

差集

Except(other)

元素

方法

说明

示例

First

第一个

First(x => x > 5)

FirstOrDefault

第一个或默认

FirstOrDefault(x => x > 10)

Last

最后一个

Last()

Single

唯一元素

Single()

SingleOrDefault

唯一或默认

SingleOrDefault()

ElementAt

指定索引

ElementAt(2)

DefaultIfEmpty

空集合默认值

DefaultIfEmpty()

量词

方法

说明

示例

Any

是否存在

Any(x => x > 5)

All

是否全部满足

All(x => x > 0)

Contains

是否包含

Contains(5)

分页

方法

说明

示例

Skip

跳过N个

Skip(10)

Take

取N个

Take(10)

SkipWhile

跳过满足条件的

SkipWhile(x => x < 5)

TakeWhile

取满足条件的

TakeWhile(x => x < 5)

连接

方法

说明

示例

Join

内连接

Join(inner, outerKey, innerKey, result)

GroupJoin

分组连接

GroupJoin(inner, outerKey, innerKey, result)

点赞
收藏
暂无评论,快来发表评论吧~