C# LINQ 所有方法详解 + 使用场景(标准查询运算符,分两类:查询语法/方法语法)

C# LINQ 所有方法详解 + 使用场景(标准查询运算符,分两类:查询语法/方法语法)

LINQ 分为延迟执行(惰性)立即执行(求值)两大类,还有聚合、元素、生成、集合修改方法,全部附代码示例与适用场景。 底层基于IEnumerable<T>/IQueryable<T>(EF 数据库查询优先用 IQueryable,减少内存加载)。

一、筛选类(延迟执行,过滤数据)

1. Where 条件过滤

作用:按布尔条件筛选元素场景:数据过滤、多条件筛选、数据库条件查询

var list = new List<int> {1,2,3,4,5}; // 筛选大于3 var res = list.Where(x => x > 3); // 4,5 // 带索引重载:筛选偶数且索引大于1 var res2 = list.Where((x,idx) => x%2==0 && idx>1);

EF 场景:db.Users.Where(u => u.Age > 18 && u.Status == 1)

2. OfType 按类型过滤

作用:集合中只取出指定类型元素,自动忽略不匹配类型场景:object 混合集合、控件集合、多态集合筛选

IList objects = new ArrayList { 1, "abc", 2, "def" }; var strs = objects.OfType<string>(); // "abc","def"

二、投影/转换类(延迟执行,映射字段/对象)

1. Select 单对象投影映射

作用:转换每一个元素,支持字段提取、对象重组、计算场景:DTO 转换、只取需要字段、字段计算

var users = new List<User>(); // 只取姓名 var names = users.Select(u => u.Name); // 匿名类 var dto = users.Select(u => new {u.Id, u.Name, Age = u.Birth.Year}); // 带索引 var indexed = users.Select((u,i) => new {Index=i, u.Name});

2. SelectMany 集合扁平化(一对多展开)

作用:把集合内嵌套集合展开,合并成一维序列场景:一对多关系、嵌套列表展开、子集合遍历

// 班级-学生 一对多 var classes = new List<Class>(); // 取出所有学生,扁平化 var allStudents = classes.SelectMany(c => c.Students); // 同时保留父对象 var classStudent = classes.SelectMany(c => c.Students, (cls,stu)=>new {cls.Name, stu.Name});

三、排序类(延迟执行,排序)

1. OrderBy / OrderByDescending 单字段升序/降序

场景:单一条件排序

var asc = users.OrderBy(u => u.Age); // 年龄升序 var desc = users.OrderByDescending(u => u.CreateTime); // 创建时间倒序

2. ThenBy / ThenByDescending 多字段次级排序

作用:主排序相同后,次要条件排序,必须跟在 OrderBy 后场景:多维度复合排序

// 先按年龄升序,同龄按分数降序 var sort = users.OrderBy(u=>u.Age).ThenByDescending(u=>u.Score);

四、分区截取类(延迟执行,取部分数据)

1. Take(n) 取前 N 条

场景:分页首页、TopN 数据、限制返回条数

var top3 = list.Take(3); // 前3个

2. TakeWhile 满足条件就持续取,遇到不满足立刻停止

场景:连续符合条件数据截取

var nums = new List<int>{1,2,4,1,5}; var take = nums.TakeWhile(x=>x<3); // 1,2(到4停止)

3. Skip(n) 跳过前 N 条

场景:分页(Skip((page-1)*size).Take(size))

var page2 = list.Skip(2).Take(3); // 跳过2条,取3条

4. SkipWhile 跳过满足条件元素,直到不满足开始取值

var skip = nums.SkipWhile(x=>x<3); // 4,1,5

五、集合去重/对比(延迟执行)

1. Distinct 去重

场景:获取不重复数据、枚举唯一值

var arr = new int[]{1,1,2,2,3}; var unique = arr.Distinct(); // 1,2,3 // 自定义比较器(复杂对象去重) var userDistinct = users.Distinct(new UserNameComparer());

2. Except 差集:A有B没有的元素

场景:对比两个集合,找出差异数据

var a = new List<int>{1,2,3}; var b = new List<int>{3,4,5}; var diff = a.Except(b); // 1,2

3. Intersect 交集:两者都存在的元素

场景:共同数据筛选、权限交集匹配

var same = a.Intersect(b); // 3

4. Union 并集,自动去重

场景:合并两个集合,去除重复

var all = a.Union(b); // 1,2,3,4,5

5. Concat 简单拼接,不去重

场景:单纯合并,允许重复

var concat = a.Concat(b); // 1,2,3,3,4,5

六、元素获取方法(立即执行,返回单个元素,无匹配抛异常)

1. First() / FirstOrDefault()

  • First():取第一个元素,空集合抛异常
  • FirstOrDefault():无数据返回类型默认值(null/0) 场景:查询单条数据、第一条匹配记录
var first = users.Where(u=>u.Id==1).FirstOrDefault();

2. Last() / LastOrDefault()

取序列最后一条,数据库 IQueryable 慎用(全查再取尾)

3. Single() / SingleOrDefault()

Single:只能有一条匹配,多/无都报错 SingleOrDefault:匹配0条返回默认,多条报错 场景:唯一主键查询(Id主键),确保只有一条数据

var user = db.Users.SingleOrDefault(u=>u.Id == 1001);

4. ElementAt(index) / ElementAtOrDefault(index)

按索引取元素,越界抛异常 / 返回默认 场景:已知下标取值

七、聚合函数(立即执行,返回数值)

1. Count() 统计总数

无参数:全部数量;带条件Count(x=>x>0)统计符合条件数量 场景:分页总条数、条件计数

int total = users.Count(u => u.Age >= 18);

2. LongCount() 返回 long 类型计数(超大集合)

3. Sum 求和

数字集合求和,支持投影Sum(x=>x.Score)

int totalScore = users.Sum(u => u.Score);

4. Min / Max 最小/最大值

int maxAge = users.Max(u => u.Age); int minScore = users.Min(u => u.Score);

5. Average 平均值

仅数值类型可用

double avgAge = users.Average(u => u.Age);

6. Aggregate 自定义聚合(万能累加器)

场景:复杂合并、字符串拼接、自定义运算

var nums = new List<int>{1,2,3,4}; // 累加 1+2+3+4 int sum = nums.Aggregate((acc,cur)=>acc+cur); // 带初始值,拼接字符串 string str = nums.Aggregate("0", (s,x)=>s+"+"+x);

八、判断存在性(立即执行,返回 bool)

1. Any() 是否存在至少一条数据

无参数:集合不为空;带条件:是否有满足条件元素推荐优先用 Any 判断存在,性能优于 Count>0

bool hasAdult = users.Any(u => u.Age >= 18);

2. All() 全部元素都满足条件

场景:批量校验数据合规性

bool allPass = users.All(u => u.Score >= 60);

3. Contains() 是否包含指定元素

基础类型直接传值;复杂对象需自定义比较器

var list = new List<int>{1,2,3}; bool exist = list.Contains(2);

九、转换类型方法(立即执行,转为固定容器)

1. ToList() 转为 List

最常用,将 IEnumerable 加载到内存,断开延迟执行 场景:查询后多次遍历、修改集合、EF 查询落地内存

var list = db.Users.Where(u=>u.Age>18).ToList();

2. ToArray() 转为 T[] 数组

3. ToDictionary(keySelector, valueSelector) 转字典

场景:主键快速索引,Id映射实体

var userDict = users.ToDictionary(k=>k.Id, v=>v.Name);

4. ToLookup() 多键一对多字典(ILookup)

一个键对应多个值,分组专用 场景:按类别分组并快速索引

var ageGroup = users.ToLookup(u => u.Age); var age20 = ageGroup[20]; // 所有20岁用户

5. AsEnumerable() / AsQueryable()

  • AsQueryable():IEnumerable 转 IQueryable,支持LINQ表达式树
  • AsEnumerable():强制切换为内存LINQ,数据库查询中局部处理 场景:EF中部分逻辑无法转SQL,强制内存计算

十、分组方法 GroupBy(延迟执行)

GroupBy 分组聚合

返回IGrouping<TKey,TElement>,支持多键、投影分组值 场景:报表分组、统计分类数据

// 单键分组:按年龄分组 var group = users.GroupBy(u => u.Age); foreach(var g in group) { int age = g.Key; int count = g.Count(); } // 多键分组(匿名类复合键) var multiGroup = users.GroupBy(u => new {u.Age, u.Gender}); // 分组同时映射元素 var groupName = users.GroupBy(u=>u.Age, u=>u.Name);

十一、生成序列方法(无数据源,创建集合,延迟)

1. Range(start, count) 生成连续数字

var nums = Enumerable.Range(1,10); // 1~10

2. Repeat(value, count) 重复生成指定值

var fiveZero = Enumerable.Repeat(0,5);

3. Empty() 返回空 IEnumerable

场景:方法返回IEnumerable,无数据时避免null

十二、zip 配对合并序列(延迟)

Zip 将两个序列按位置一一配对,长度取最短

场景:两个列表一一对应合并

var a = new List<int>{1,2,3}; var b = new List<string>{"a","b","c"}; var zip = a.Zip(b,(x,y)=>new{Num=x,Str=y});

关键核心知识点

1. 延迟执行 vs 立即执行 区分

延迟(仅定义查询,遍历才执行)

Where/Select/SelectMany/OrderBy/Take/Skip/Distinct/GroupBy/Concat/Union/Except/Intersect/Zip/OfType 优点:多次拼接查询只会执行一次;EF 可拼接SQL优化

立即(调用时直接执行查询加载数据)

ToList/ToArray/ToDictionary/Count/Sum/Min/Max/Average/Aggregate/First/Any/All/Single/Contains 一旦调用,SQL/内存计算立刻执行,结果存入容器

2. IQueryable VS IEnumerable 使用场景

  1. 数据库查询(EF Core)优先 IQueryable:Where/Select/Take 会翻译为SQL,只拉取需要数据,性能高
  2. 复杂逻辑无法转SQL(本地方法、自定义函数):加.AsEnumerable()切换内存LINQ
  3. 内存集合(List/Array)只能用 IEnumerable

3. 高频业务场景速查表

需求推荐LINQ方法
分页Skip + Take + Count
DTO转换Select 匿名类/实体
一对多展开SelectMany
主键查单条SingleOrDefault
判断数据存在Any()
分组统计GroupBy + Count/Sum
去重Distinct
集合对比差异Except/Intersect/Union
多条件排序OrderBy + ThenBy
列表转字典索引ToDictionary
内存落地查询结果ToList()

4. 避坑提醒

  1. IQueryable 不要在循环内多次 ToList(),会重复查库
  2. First()/Single() 无数据抛异常,业务统一用 *OrDefault
  3. OrderBy 后必须 ThenBy,不能连续两个 OrderBy(后者覆盖前者)
  4. Count() > 0 性能远差于 Any(),判断存在一律 Any
  5. EF 中 Last() 会拉取全表再取最后一条,大数据禁用,改用 OrderByDescending + FirstOrDefault