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. 考虑可读性:复杂查询适当分段或使用注释

相关推荐
写代码的橘子n7 小时前
IPV6复习(基础入手版)
运维·服务器·网络
向日葵.8 小时前
中间件交接文档
linux·运维·服务器
TG:@yunlaoda360 云老大8 小时前
华为云国际站代理商TaurusDB的读写分离是如何实现的?
服务器·数据库·华为云
Blurpath8 小时前
什么是 ISP 代理?双 ISP 代理又是什么?一文讲清原理与应用场景
服务器·网络·ip代理·静态代理·住宅代理
TG:@yunlaoda360 云老大8 小时前
华为云国际站代理商如何配置GaussDB的读写分离权重?
服务器·华为云·gaussdb
小快说网安8 小时前
抗 DDoS 防护在等保测评中的权重提升:云服务器如何通过防护能力加分?
运维·服务器·ddos·等保测评
gaize12139 小时前
腾讯云云服务器核心优势
服务器·腾讯云
陌路209 小时前
TCP连接如何确保其可靠性
运维·服务器
天远云服9 小时前
Go 语言实战:手撸 AES-128-CBC 加密,对接天远金融风控 API
大数据·服务器·网络·golang