LINQ语法

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 转换结果类型

最佳实践建议

  1. 选择合适的语法:简单查询用查询语法,复杂操作用方法语法

  2. 注意性能:数据库查询时使用合适的索引,避免N+1查询问题

  3. 使用延迟执行优势:构建动态查询链

  4. 及时释放资源:数据库上下文及时Dispose

  5. 考虑可读性:复杂查询适当分段或使用注释

相关推荐
wanhengidc1 小时前
服务器都有哪些优缺点?
运维·服务器·科技·智能手机·云计算
FLPGYH1 小时前
OpenBMC 中 IPMI 协议全解析:架构、组件与实战应用
服务器·架构·openbmc·ipmi
帅得不敢出门1 小时前
Android8 Framework实现Ntp服务器多域名轮询同步时间
android·java·服务器·python·framework·github
视觉装置在笑7131 小时前
awk 基础知识和进阶用法
linux·运维·服务器·正则表达式
无线图像传输研究探索1 小时前
国标28181平台与TCP对讲:从“不支持”到“实现路径”的完整解析(5G单兵图传、布控球)
运维·服务器·网络·5g·无人机·单兵图传·无人机图传
长沙古天乐1 小时前
Spring Boot应用中配置消费端随服务启动循环消费消息
spring boot·后端·linq
小糖学代码1 小时前
LLM系列:1.python入门:1.初识python
服务器·开发语言·人工智能·python·ai
Kaede62 小时前
如何快速排查服务器宕机原因
运维·服务器
深圳市恒讯科技2 小时前
如何选服务器硬件:CPU、内存与 NVMe 的性能与成本权衡
运维·服务器