C#怎么使用LINQ Distinct去重 C#如何用LINQ对集合按指定字段去重和自定义比较器【语法】
C# LINQ Distinct去重方法详解:按字段去重与自定义比较器完整指南

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
Distinct 默认去重机制:值类型与引用类型的核心差异
在C#中直接对 List 这类自定义引用类型集合调用 .Distinct() 方法,通常无法实现按字段内容去重的效果。这是因为默认的 Distinct 操作比较的是对象的内存引用地址,而非对象内部属性的值。即使两个 Person 实例的 Name 和 Age 属性完全相同,只要它们是不同的对象实例,就会被视为不同元素而保留。
典型问题表现:调用 .Distinct() 后集合元素数量未减少,调试时发现数据重复问题依然存在。
- 基础值类型(如
int、double)和string可直接使用.Distinct()实现去重 - 自定义类必须显式定义比较逻辑,否则默认基于引用的比较行为无法满足业务需求
- 字符串虽然是引用类型,但因其重写了
Equals和GetHashCode方法,故List能够正确工作.Distinct()
Distinct方法对引用类型默认执行对象引用去重,需自定义比较逻辑;值类型和已重写Equals的引用类型(如string)可直接使用;单字段去重推荐GroupBy+First组合,多字段去重可采用匿名类型+DistinctBy(.NET 6+)或GroupBy方案,复杂业务场景需实现IEqualityComparer接口。
按单个属性去重:GroupBy + First 组合方案
无需编写比较器代码,也不必创建额外类,这种方法特别适合快速按 Id、Name 等唯一性字段筛选“首次出现的记录”。
应用示例:从产品列表中提取每个 CategoryId 分类下的第一个 Product 对象
var uniqueByCategory = list
.GroupBy(x => x.CategoryId)
.Select(g => g.First())
.ToList();
GroupBy操作按指定字段分组后,每组至少包含一个元素,使用First()选取首个元素,逻辑清晰易懂- 性能方面虽略低于
Distinct配合自定义比较器(需完成分组操作),但开发效率更高且不易出错 - 若需获取“最新记录”(如按
CreatedTime降序排列的首条),可将First()替换为OrderByDescending(x => x.CreatedTime).First()
按多个属性组合去重:匿名类型与Distinct的优雅方案
当需要基于 FirstName 和 LastName 等多个字段的组合进行去重时,利用匿名类型自动实现的相等性比较是最简洁的方案,无需手动编写比较器。
代码示例:去除姓名(姓+名)重复的用户记录
var uniqueByName = users
.DistinctBy(u => new { u.FirstName, u.LastName })
.ToList();
⚠️ 重要说明:DistinctBy 是 .NET 6 及以上版本新增的扩展方法(位于 System.Linq 命名空间),旧版本框架需使用 GroupBy 替代实现:
var uniqueByName = users
.GroupBy(u => new { u.FirstName, u.LastName })
.Select(g => g.First())
.ToList();
- 匿名类型的属性名称和大小写必须完全一致,
new { F = u.FirstName }与new { FirstName = u.FirstName }被视为不同的类型 - 当字段值为
null时,匿名类型能够正确处理空值比较 - 此方案不支持动态字段数量(如运行时传入字段名列表),此类场景必须通过自定义
IEqualityComparer实现
自定义比较器实现:IEqualityComparer 接口的完整控制方案
当业务需要复杂比较逻辑(如忽略大小写、按子字符串匹配、多级优先级判断)或需兼容旧框架(.NET Framework / .NET 5及以下)时,必须通过实现 IEqualityComparer 接口来提供完全可控的比较机制。
示例:实现按 Code 字段忽略大小写的产品去重比较器
public class CodeComparer : IEqualityComparer{ public bool Equals(Product x, Product y) => x?.Code?.Equals(y?.Code, StringComparison.OrdinalIgnoreCase) == true; public int GetHashCode(Product obj) => obj?.Code?.ToLowerInvariant().GetHashCode() ?? 0; }
调用方式:
var unique = products.Distinct(new CodeComparer()).ToList();
GetHashCode方法的实现必须与Equals方法的逻辑严格一致,否则Distinct可能出现漏判或误判- 必须显式处理空值(
null)情况,避免引发NullReferenceException异常 - 比较器实例应当复用,避免在循环或高频调用路径中重复创建新实例
最易被忽视的关键点:Distinct 操作采用延迟执行模式,但去重的准确性完全依赖于所提供的比较逻辑是否全面覆盖业务场景。例如仅按 Email 字段去重却未处理前后空格或大小写差异,可能导致数据重复问题被隐藏。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
怎么利用 System.err 输出错误流并在控制台中以醒目的颜色标记(取决于终端)
怎么利用 System err 输出错误流并在控制台中以醒目的颜色标记(取决于终端) System err 默认行为不带颜色,终端是否显示颜色取决于自身支持 首先得明确一点:System err 本质上只是 Ja va 标准库里的一个 PrintStream 对象。它本身并不负责“颜色”这种花哨的玩
如何在 Java 中使用 ThreadLocal.remove() 确保在线程池复用场景下不会发生数据污染
如何在 Ja va 中使用 ThreadLocal remove() 确保在线程池复用场景下不会发生数据污染 说到线程池和 ThreadLocal 的搭配使用,一个看似不起眼、实则极易“踩坑”的细节就是数据清理。想象一下,你精心设计的线程池正在高效运转,却因为某个任务留下的“数据尾巴”,导致后续任务
怎么利用 Arrays.asList() 转换出的“受限列表”理解其对 add() 等修改操作的限制
Arrays asList():一个“受限”但实用的列表视图 在Ja va开发中,Arrays asList()是一个高频使用的方法,但你是否真正了解它返回的是什么?一个常见的误解是,它直接生成了一个标准的ArrayList。事实并非如此。 简单来说,Arrays asList()返回的并非我们熟悉
如何在 Java 中利用 try-catch 实现对“软错误”的平滑感知与非侵入式监控日志记录
如何在 Ja va 中利用 try-catch 实现对“软错误”的平滑感知与非侵入式监控日志记录 在 Ja va 开发中,我们常常会遇到一些“软错误”——它们不会让程序直接崩溃,却可能悄悄影响业务的正确性或用户体验。比如,调用第三方 API 时返回了空响应、缓存查询未命中、配置文件里某个非关键项缺失
Django怎么防止Celery任务重复执行_Python结合Redis实现分布式锁
Django怎么防止Celery任务重复执行:Python结合Redis实现分布式锁 你遇到过吗?明明只发了一次任务,后台却执行了两次。这不是代码写错了,而是分布式环境下一个经典的老朋友:多个worker同时抢到了同一个活儿。 为什么Celery任务会重复执行 问题的根源在于竞争。想象一下,多个Ce
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

