目录
5.3 集合操作符 Union、Intersect、Except
前言
在 C# 的世界中,LINQ(Language Integrated Query,语言集成查询)是一项革命性的技术,它为数据查询和操作提供了一种统一、声明式的方式。无论是操作内存中的集合、数据库中的数据,还是 XML、JSON 等其他数据源,LINQ 都能让代码更加简洁、易读且易于维护。本文将带你深入了解 LINQ 的用法,从基础到高级,感受它的魅力。
一、LINQ 初探:基本概念与优势
1.1 什么是 LINQ?
LINQ 是一组扩展方法,它允许你使用类似 SQL 的语法来查询各种数据源。LINQ 查询表达式被编译为方法调用,这些方法可以针对不同的数据源实现。
1.2 LINQ 的优势
- 统一语法:无论数据源是集合、数据库还是其他类型,都可以使用相同的 LINQ 语法进行查询。
- 类型安全:在编译时就能发现错误,而不是在运行时。
- 可读性强:LINQ 查询表达式接近自然语言,易于理解和维护。
- 可组合性:可以将多个 LINQ 查询组合在一起,形成更复杂的查询。
二、LINQ 查询语法基础
2.1 使用 from...select
语法
这是 LINQ 中最常用的查询语法,类似于 SQL 的 SELECT
语句。
using System;
using System.Linq; // 命名空间包含了许多用于实现 LINQ 功能的扩展方法
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> names = new List<string> { "Alice", "Bob", "Charlie", "David" };
// 查询以字母 'A' 开头的名字
var query = from name in names
where name.StartsWith("A")
select name;
foreach (var name in query)
{
Console.WriteLine(name); // 输出: Alice
}
}
}
2.2 延迟执行
LINQ 查询是延迟执行的,这意味着查询在枚举结果之前不会实际执行,你需要以某种方式枚举它,他才得以真正地被执行。这可以提高性能,特别是在处理大量数据时。
List<int> numbers = Enumerable.Range(1, 100).ToList();
var evenNumbersQuery = from num in numbers
where num % 2 == 0
select num;
// 此时查询尚未执行
Console.WriteLine("Query created but not executed yet.");
// 枚举结果时查询执行
foreach (var num in evenNumbersQuery)
{
Console.WriteLine(num); // 输出 2, 4, 6, ..., 100
}
三、LINQ 常用操作符
3.1 筛选操作符 Where
Where
操作符用于根据条件筛选元素。
List<int> numbers = Enumerable.Range(1, 10).ToList();
IEnumerable<int> filteredNumbers = numbers.Where(num => num > 5);
foreach (var num in filteredNumbers)
{
Console.WriteLine(num); // 输出 6, 7, 8, 9, 10
}
3.2 投影操作符 Select
Select
操作符用于将序列中的每个元素投影到一个新的形式。
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
var nameLengths = names.Select(name => name.Length);
foreach (var length in nameLengths)
{
Console.WriteLine(length); // 输出 5, 3, 7
}
3.3 排序操作符 OrderBy
和 ThenBy
OrderBy
用于按指定键对序列进行升序排序,ThenBy
用于在主排序后进行次级排序。
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 20 },
new Person { Name = "Charlie", Age = 25 }
};
// 按年龄升序排序,年龄相同则按姓名升序排序
var sortedPeople = people.OrderBy(p => p.Age).ThenBy(p => p.Name);
foreach (var person in sortedPeople)
{
Console.WriteLine($"{person.Name}, {person.Age}");
// 输出:
// Bob, 20
// Alice, 25
// Charlie, 25
}
拓展: 按年龄降序排序,年龄相同则按姓名降序排序
var sortedPeople = people.OrderByDescending(p => p.Age).ThenByDescending(p => p.Name);
3.4 分组操作符 GroupBy
GroupBy
用于将序列中的元素按指定键进行分组。
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Department = "HR" },
new Person { Name = "Bob", Department = "IT" },
new Person { Name = "Charlie", Department = "HR" }
};
var groupedByDepartment = people.GroupBy(p => p.Department);
foreach (var group in groupedByDepartment)
{
Console.WriteLine($"Department: {group.Key}");
foreach (var person in group)
{
Console.WriteLine($" {person.Name}");
}
// 输出:
// Department: HR
// Alice
// Charlie
// Department: IT
// Bob
}
3.5 聚合操作符 Sum
、Average
、Max
、Min
这些操作符用于对序列中的元素进行聚合计算。
List<int> numbers = Enumerable.Range(1, 5).ToList();
int sum = numbers.Sum(); // 求和
double average = numbers.Average(); // 平均值
int max = numbers.Max(); // 最大值
int min = numbers.Min(); // 最小值
Console.WriteLine($"Sum: {sum}, Average: {average}, Max: {max}, Min: {min}");
// 输出: Sum: 15, Average: 3, Max: 5, Min: 1
四、LINQ 方法语法与查询语法对比
LINQ 提供了两种语法:查询语法和方法语法。查询语法更接近 SQL,而方法语法则更接近传统的 C# 方法调用。
4.1 查询语法示例
var querySyntax = from num in numbers
where num % 2 == 0
select num * 2;
4.2 方法语法示例
var methodSyntax = numbers.Where(num => num % 2 == 0).Select(num => num * 2);
两种语法在功能上是等价的,选择哪种语法主要取决于个人偏好和代码的可读性。
五、LINQ 高级特性
5.1 连接操作符 Join
Join
用于将两个序列基于指定的键进行连接。
List<Student> students = new List<Student>
{
new Student { Id = 1, Name = "Alice" },
new Student { Id = 2, Name = "Bob" }
};
List<Enrollment> enrollments = new List<Enrollment>
{
new Enrollment { StudentId = 1, Course = "Math" },
new Enrollment { StudentId = 2, Course = "Science" }
};
var joinedData = from student in students
join enrollment in enrollments on student.Id equals enrollment.StudentId
select new { student.Name, enrollment.Course };
foreach (var item in joinedData)
{
Console.WriteLine($"{item.Name} is enrolled in {item.Course}");
// 输出:
// Alice is enrolled in Math
// Bob is enrolled in Science
}
class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
class Enrollment
{
public int StudentId { get; set; }
public string Course { get; set; }
}
5.2 分区操作符 Skip
和 Take
Skip
用于跳过序列中的前 N 个元素,Take
用于从序列中获取前 N 个元素。这两个操作符常用于分页。
List<int> numbers = Enumerable.Range(1, 20).ToList();
// 获取第二页的数据(每页 5 条记录)
int pageSize = 5;
int pageNumber = 2;
var pagedData = numbers.Skip((pageNumber - 1) * pageSize).Take(pageSize);
foreach (var num in pagedData)
{
Console.WriteLine(num); // 输出 6, 7, 8, 9, 10
}
5.3 集合操作符 Union
、Intersect
、Except
这些操作符用于对集合进行并集、交集和差集操作。
List<int> set1 = new List<int> { 1, 2, 3, 4 };
List<int> set2 = new List<int> { 3, 4, 5, 6 };
var union = set1.Union(set2); // 并集: 1, 2, 3, 4, 5, 6
var intersect = set1.Intersect(set2); // 交集: 3, 4
var except = set1.Except(set2); // 差集(set1 - set2): 1, 2
Console.WriteLine("Union:");
foreach (var num in union) Console.Write(num + " "); // 输出: Union: 1 2 3 4 5 6
Console.WriteLine("\nIntersect:");
foreach (var num in intersect) Console.Write(num + " "); // 输出: Intersect: 3 4
Console.WriteLine("\nExcept:");
foreach (var num in except) Console.Write(num + " "); // 输出: Except: 1 2
六、总结
LINQ 是 C# 中一项非常强大的功能,它极大地简化了数据查询和操作的代码。通过本文的介绍,你应该已经对 LINQ 的基本概念、常用操作符、与数据库的集成以及高级特性有了深入的了解。
在实际开发中,合理使用 LINQ 可以让你的代码更加简洁、易读且易于维护。无论是处理内存中的集合还是数据库中的数据,LINQ 都能提供一种统一、高效的方式。希望本文能帮助你更好地掌握 LINQ,并在你的项目中发挥它的优势。
友友们如果对LINQ操作SQL数据库有兴趣的话,欢迎在评论区留言,后续我可以做一期详细教学。