LINQ概述
LINQ (Language Integrated Query) 是.NET Framework的一项功能,它允许在C#和VB.NET中直接编写查询,用于查询各种数据源。
基本结构
csharp
from item in dataSource
where condition
select item;
查询语法 vs 方法语法
查询语法 (Query Syntax)
更接近SQL,可读性更强
csharp
// 示例:查询偶数
var numbers = new int[] { 1, 2, 3, 4, 5, 6 };
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
// 结果: [2, 4, 6]
方法语法 (Method Syntax)
使用扩展方法和Lambda表达式,更灵活
csharp
var evenNumbers = numbers.Where(num => num % 2 == 0);
// 结果: [2, 4, 6]
标准查询操作符
1. 筛选操作符 (Filtering)
Where - 根据条件筛选
csharp
// 查询语法
var result = from p in products
where p.Price > 100
select p;
// 方法语法
var result = products.Where(p => p.Price > 100);
OfType - 根据类型筛选
csharp
object[] objects = { 1, "hello", 2, "world", 3.14 };
var strings = objects.OfType<string>();
// 结果: ["hello", "world"]
2. 投影操作符 (Projection)
Select - 选择/转换元素
csharp
// 查询语法
var names = from p in products
select p.Name;
// 方法语法
var names = products.Select(p => p.Name);
SelectMany - 展平嵌套集合
csharp
class Student {
public List<string> Courses { get; set; }
}
var students = new List<Student> { /* ... */ };
var allCourses = students.SelectMany(s => s.Courses);
3. 排序操作符 (Ordering)
OrderBy / OrderByDescending - 升序/降序排序
csharp
// 查询语法
var sorted = from p in products
orderby p.Price descending
select p;
// 方法语法
var sorted = products.OrderBy(p => p.Price);
var sortedDesc = products.OrderByDescending(p => p.Price);
ThenBy / ThenByDescending - 次要排序
csharp
var sorted = products
.OrderBy(p => p.Category)
.ThenByDescending(p => p.Price);
4. 分组操作符 (Grouping)
GroupBy - 分组数据
csharp
// 查询语法
var grouped = from p in products
group p by p.Category into g
select new { Category = g.Key, Products = g };
// 方法语法
var grouped = products.GroupBy(p => p.Category);
ToLookup - 创建查找表(立即执行)
csharp
var lookup = products.ToLookup(p => p.Category);
var electronics = lookup["Electronics"];
5. 连接操作符 (Joining)
Join - 内连接
csharp
var students = new List<Student> { /* ... */ };
var courses = new List<Course> { /* ... */ };
// 查询语法
var query = from s in students
join c in courses on s.CourseId equals c.Id
select new { s.Name, c.CourseName };
// 方法语法
var query = students.Join(courses,
s => s.CourseId,
c => c.Id,
(s, c) => new { s.Name, c.CourseName });
GroupJoin - 分组连接
csharp
var query = from c in categories
join p in products on c.Id equals p.CategoryId into productGroup
select new { Category = c.Name, Products = productGroup };
Zip - 合并两个序列
csharp
var numbers = new[] { 1, 2, 3 };
var words = new[] { "one", "two", "three" };
var result = numbers.Zip(words, (n, w) => $"{n}: {w}");
// 结果: ["1: one", "2: two", "3: three"]
6. 集合操作符 (Set Operations)
Distinct - 去重
csharp
int[] numbers = { 1, 2, 2, 3, 3, 3, 4 };
var unique = numbers.Distinct();
// 结果: [1, 2, 3, 4]
Union - 并集
csharp
var list1 = new[] { 1, 2, 3 };
var list2 = new[] { 3, 4, 5 };
var union = list1.Union(list2);
// 结果: [1, 2, 3, 4, 5]
Intersect - 交集
csharp
var intersect = list1.Intersect(list2);
// 结果: [3]
Except - 差集
csharp
var except = list1.Except(list2);
// 结果: [1, 2]
7. 分区操作符 (Partitioning)
Skip / Take - 跳过/获取指定数量元素
csharp
var page2 = products.Skip(10).Take(10); // 获取第2页,每页10条
SkipWhile / TakeWhile - 条件跳过/获取
csharp
int[] numbers = { 1, 3, 5, 2, 4, 6 };
var result = numbers.SkipWhile(n => n < 4);
// 结果: [5, 2, 4, 6]
8. 聚合操作符 (Aggregation)
Count / LongCount - 计数
csharp
int count = products.Count();
int expensiveCount = products.Count(p => p.Price > 100);
Sum / Average / Min / Max - 统计
csharp
var total = products.Sum(p => p.Price);
var average = products.Average(p => p.Price);
var minPrice = products.Min(p => p.Price);
var maxPrice = products.Max(p => p.Price);
Aggregate - 自定义聚合
csharp
int[] numbers = { 1, 2, 3, 4, 5 };
int product = numbers.Aggregate(1, (acc, n) => acc * n);
// 结果: 120 (1*2*3*4*5)
9. 元素操作符 (Element Operators)
First / FirstOrDefault - 获取第一个元素
csharp
var first = products.First();
var firstOrDefault = products.FirstOrDefault(p => p.Price > 1000);
Last / LastOrDefault - 获取最后一个元素
csharp
var last = products.Last();
Single / SingleOrDefault - 获取唯一元素
csharp
var single = products.Single(p => p.Id == 1);
ElementAt / ElementAtOrDefault - 获取指定位置元素
csharp
var third = products.ElementAt(2);
10. 转换操作符 (Conversion)
ToArray / ToList - 转换为数组/列表
csharp
List<Product> productList = products.ToList();
Product[] productArray = products.ToArray();
ToDictionary - 转换为字典
csharp
var dict = products.ToDictionary(p => p.Id, p => p.Name);
Cast - 强制类型转换
csharp
ArrayList list = new ArrayList { 1, 2, 3 };
var ints = list.Cast<int>();
AsEnumerable / AsQueryable - 转换为可枚举/可查询
csharp
var enumerable = products.AsEnumerable();
var queryable = products.AsQueryable();
11. 生成操作符 (Generation)
Range - 生成数字序列
csharp
var numbers = Enumerable.Range(1, 10);
// 结果: [1, 2, 3, ..., 10]
Repeat - 重复元素
csharp
var repeated = Enumerable.Repeat("Hello", 3);
// 结果: ["Hello", "Hello", "Hello"]
Empty - 空序列
csharp
var empty = Enumerable.Empty<int>();
12. 相等性操作符 (Equality)
SequenceEqual - 序列相等比较
csharp
var list1 = new[] { 1, 2, 3 };
var list2 = new[] { 1, 2, 3 };
bool equal = list1.SequenceEqual(list2); // true
13. 量词操作符 (Quantifiers)
Any - 是否存在满足条件的元素
csharp
bool hasExpensive = products.Any(p => p.Price > 1000);
bool hasProducts = products.Any(); // 检查是否非空
All - 是否所有元素都满足条件
csharp
bool allPositive = numbers.All(n => n > 0);
Contains - 是否包含指定元素
csharp
bool hasProduct = products.Contains(specificProduct);
延迟执行与立即执行
延迟执行 (Deferred Execution)
查询不会立即执行,只有在遍历结果时才会执行
csharp
var query = products.Where(p => p.Price > 100); // 未执行
foreach (var p in query) // 此时执行
{
Console.WriteLine(p.Name);
}
立即执行 (Immediate Execution)
使用聚合方法或转换方法会立即执行
csharp
List<Product> list = products.Where(p => p.Price > 100).ToList(); // 立即执行
int count = products.Count(); // 立即执行
LINQ to Objects小栗子
完整示例:学生成绩查询
csharp
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public List<CourseGrade> Grades { get; set; }
}
public class CourseGrade
{
public string CourseName { get; set; }
public int Score { get; set; }
}
// 数据准备
var students = new List<Student>
{
new Student { Id = 1, Name = "Alice", Age = 20, Grades = new List<CourseGrade>
{ new CourseGrade { CourseName = "Math", Score = 85 }, new CourseGrade { CourseName = "English", Score = 90 } } },
new Student { Id = 2, Name = "Bob", Age = 21, Grades = new List<CourseGrade>
{ new CourseGrade { CourseName = "Math", Score = 78 }, new CourseGrade { CourseName = "English", Score = 82 } } },
new Student { Id = 3, Name = "Charlie", Age = 19, Grades = new List<CourseGrade>
{ new CourseGrade { CourseName = "Math", Score = 92 }, new CourseGrade { CourseName = "English", Score = 88 } } }
};
// 复杂查询示例
var query = from s in students
where s.Age >= 20
from g in s.Grades
where g.Score >= 80
group g by s.Name into studentGrades
select new
{
StudentName = studentGrades.Key,
AverageScore = studentGrades.Average(g => g.Score),
HighScore = studentGrades.Max(g => g.Score)
};
// 方法语法等价写法
var query2 = students
.Where(s => s.Age >= 20)
.SelectMany(s => s.Grades
.Where(g => g.Score >= 80)
.Select(g => new { Student = s, Grade = g }))
.GroupBy(x => x.Student.Name)
.Select(g => new
{
StudentName = g.Key,
AverageScore = g.Average(x => x.Grade.Score),
HighScore = g.Max(x => x.Grade.Score)
});
LINQ to SQL/Entity Framework
基本查询
csharp
using (var context = new SchoolContext())
{
// 查询语法
var query = from s in context.Students
where s.EnrollmentDate.Year == 2020
orderby s.LastName
select new { s.FirstName, s.LastName, s.EnrollmentDate };
// 方法语法
var query2 = context.Students
.Where(s => s.EnrollmentDate.Year == 2020)
.OrderBy(s => s.LastName)
.Select(s => new { s.FirstName, s.LastName, s.EnrollmentDate });
// 注意:延迟执行,实际SQL在遍历时生成
foreach (var student in query)
{
Console.WriteLine(student);
}
}
连接查询
csharp
var query = from s in context.Students
join e in context.Enrollments on s.StudentId equals e.StudentId
join c in context.Courses on e.CourseId equals c.CourseId
select new { s.Name, c.Title, e.Grade };
进阶主题
1. 复合查询
csharp
var complexQuery =
from p in products
join c in categories on p.CategoryId equals c.Id
where p.Price > 50 && c.IsActive
group p by c.Name into g
orderby g.Count() descending
select new
{
Category = g.Key,
TotalProducts = g.Count(),
AveragePrice = g.Average(p => p.Price),
Products = g.Select(p => p.Name)
};
2. 动态查询
csharp
IQueryable<Product> query = context.Products;
if (minPrice.HasValue)
query = query.Where(p => p.Price >= minPrice.Value);
if (!string.IsNullOrEmpty(category))
query = query.Where(p => p.Category == category);
if (sortByPrice)
query = query.OrderBy(p => p.Price);
3. 性能优化技巧
csharp
// 使用 AsNoTracking 提高只读查询性能
var products = context.Products.AsNoTracking().ToList();
// 使用 Select 只获取需要的字段
var names = context.Products.Select(p => p.Name).ToList();
// 批量操作时使用合适的批处理大小
context.BulkInsert(products, options => options.BatchSize = 1000);
4. 自定义扩展方法
csharp
public static class LinqExtensions
{
public static IEnumerable<T> WhereIf<T>(
this IEnumerable<T> source,
bool condition,
Func<T, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
// 使用
var result = products
.WhereIf(minPrice.HasValue, p => p.Price >= minPrice.Value)
.WhereIf(!string.IsNullOrEmpty(category), p => p.Category == category);
总结
| 操作类型 | 常用操作符 | 特点 |
|---|---|---|
| 筛选 | Where, OfType |
条件过滤数据 |
| 投影 | Select, SelectMany |
转换数据形状 |
| 排序 | OrderBy, ThenBy |
数据排序 |
| 分组 | GroupBy, ToLookup |
数据分组 |
| 连接 | Join, GroupJoin |
合并数据源 |
| 集合 | Union, Intersect |
集合运算 |
| 聚合 | Count, Sum, Average |
数据统计 |
| 元素 | First, Single, ElementAt |
获取特定元素 |
| 转换 | ToList, ToArray, ToDictionary |
转换结果类型 |
最佳实践建议
-
选择合适的语法:简单查询用查询语法,复杂操作用方法语法
-
注意性能:数据库查询时使用合适的索引,避免N+1查询问题
-
使用延迟执行优势:构建动态查询链
-
及时释放资源:数据库上下文及时Dispose
-
考虑可读性:复杂查询适当分段或使用注释