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 字段去重却未处理前后空格或大小写差异,可能导致数据重复问题被隐藏。
相关攻略
C ReadOnlySpan 使用指南:高性能只读内存切片优化技巧【高级教程】 在 NET 高性能编程实践中,尤其是在字符串处理场景,一个公认的高效策略是:直接采用 ReadOnlySpan 来替代传统的 string 参数以及中间的 Substring 调用。这是目前实现零分配、低开销处理的最
SQL Server分页首选OFFSET-FETCH,需配合ORDER BY且参数化传值;EF Core用Skip Take自动翻译,避免内存分页;大数据量时应改用游标分页。 SQL Server 中用 OFFSET-FETCH 做分页最直接 说到在SQL Server里做分页,2012及以上版本提
C 万级数据批量插入:SqlBulkCopy 实战精要 在C 中进行大规模数据插入,性能是首要考量。当数据量达到万级甚至更高时,常规的逐条插入方法会迅速成为性能瓶颈。那么,有没有一种既高效又稳定的解决方案呢?答案是肯定的。 用 SqlBulkCopy 实现高速批量插入 开门见山地说,在C 生态中,
C 中使用TestContainers进行集成测试:最佳实践与常见坑点 想在 NET 里玩转 TestContainers?这事儿说简单也简单,说麻烦也麻烦。简单在于,它确实能让你用几行代码就拉起一个数据库或中间件进行测试;麻烦在于,从环境配置到代码编写,每一步都有几个“经典”的坑在等着你。今天,
C WPF Canvas画布绘图完全指南:代码动态绘制图形与连线详解 Canvas直接添加子元素导致错位或不显示的解决方案 许多C 开发者在初次使用WPF Canvas控件进行动态绘图时,常会遇到一个典型问题:为何通过代码添加的Rectangle矩形或Line线条无法正常显示,或者出现位置偏移?
热门专题
热门推荐
商业帝国大亨:一款点击就能征服宇宙的财富游戏? 近期,手游圈的目光似乎被一款名为《商业帝国大亨》的新作吸引了。不少玩家都在询问:这款游戏到底好不好玩?值不值得投入时间?今天,我们就来深入剖析一下它的玩法核心与特色,看看它能否满足你对“商业帝国”的想象。 1 核心玩法评析:从点击屏幕到宇宙财团 如果
异环一咖舍店铺装修方案分享:店铺经营怎么装修 在《异环》的世界里,经营自己的店铺无疑是件充满乐趣的事。看着人气攀升、收入增长,那份成就感不言而喻。不过,很多新手玩家容易踏入一个误区:一上来就冲着最华丽的摆件去,结果投入巨大,收益提升却未必理想。今天,我们就来聊聊如何用最精明的策略,搞定你的“一咖舍”
鸣潮3 3版本声骸管理方案推荐 随着鸣潮3 3版本的到来,一次全面的声骸系统更新在所难免。特别是针对那些拥有特殊机制的角色,如何高效管理你的声骸库存,成了不少指挥官当前的头等大事。好消息是,新版本支持通过方案码一键导入配置,这无疑大大提升了效率。那么,当前版本有哪些值得关注的方案,又该如何灵活运用呢
梦幻西游神木林175级装备搭配推荐 先来看头盔的选择。这是一件130级的罗汉金钟男头,套装点化成了蜃气妖,并且打上了13锻月亮石。对于神木林这样的法系门派来说,蜃气妖套能直接提升灵力,是核心选择之一。而罗汉金钟这个特技,在高端任务和PK中的重要性不言而喻,关键时刻一个罗汉,往往能扭转战局。用高锻数的
梦幻西游魔王寨175装备搭配推荐 先来看头盔的选择。一件160级附带光辉之甲特技、且激活了长眉灵猴套装效果的头盔,无疑是法系门派的上乘之选。更难得的是,它还额外附加了4 58%的法术暴击伤害属性。为了最大化生存能力,这颗头盔被打上了16锻月亮石,将防御堆砌到了一个相当可观的程度。对于追求极致输出的魔





