游乐游手机版
首页/编程语言/文章详情

C#怎么使用LINQ Distinct去重 C#如何用LINQ对集合按指定字段去重和自定义比较器【语法】

时间:2026-05-06 07:34
C LINQ Distinct去重方法详解:按字段去重与自定义比较器完整指南 Distinct 默认去重机制:值类型与引用类型的核心差异 在C 中直接对 List 这类自定义引用类型集合调用 Distinct() 方法,通常无法实现按字段内容去重的效果。这是因为默认的 Distinct 操作比较

C# LINQ Distinct去重方法详解:按字段去重与自定义比较器完整指南

C#怎么使用LINQ Distinct去重 C#如何用LINQ对集合按指定字段去重和自定义比较器【语法】

Distinct 默认去重机制:值类型与引用类型的核心差异

在C#中直接对 List 这类自定义引用类型集合调用 .Distinct() 方法,通常无法实现按字段内容去重的效果。这是因为默认的 Distinct 操作比较的是对象的内存引用地址,而非对象内部属性的值。即使两个 Person 实例的 NameAge 属性完全相同,只要它们是不同的对象实例,就会被视为不同元素而保留。

典型问题表现:调用 .Distinct() 后集合元素数量未减少,调试时发现数据重复问题依然存在。

  • 基础值类型(如 intdouble)和 string 可直接使用 .Distinct() 实现去重
  • 自定义类必须显式定义比较逻辑,否则默认基于引用的比较行为无法满足业务需求
  • 字符串虽然是引用类型,但因其重写了 EqualsGetHashCode 方法,故 List.Distinct() 能够正确工作
Distinct方法对引用类型默认执行对象引用去重,需自定义比较逻辑;值类型和已重写Equals的引用类型(如string)可直接使用;单字段去重推荐GroupBy+First组合,多字段去重可采用匿名类型+DistinctBy(.NET 6+)或GroupBy方案,复杂业务场景需实现IEqualityComparer接口。

按单个属性去重:GroupBy + First 组合方案

无需编写比较器代码,也不必创建额外类,这种方法特别适合快速按 IdName 等唯一性字段筛选“首次出现的记录”。

应用示例:从产品列表中提取每个 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的优雅方案

当需要基于 FirstNameLastName 等多个字段的组合进行去重时,利用匿名类型自动实现的相等性比较是最简洁的方案,无需手动编写比较器。

代码示例:去除姓名(姓+名)重复的用户记录

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 字段去重却未处理前后空格或大小写差异,可能导致数据重复问题被隐藏。

来源:https://www.php.cn/faq/2316981.html
上一篇如何在继承 FPDF 的自定义类中正确使用 FPDI 导入 PDF 页面 下一篇C++实现高性能字符串拼接 _ std::ostringstream与reserve对比【干货】
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
CentOS与Golang打包常见兼容性问题探讨
编程语言 · 2026-07-01

CentOS与Golang打包常见兼容性问题探讨

CentOS与Golang打包的兼容性问题集中在glibc版本不匹配、交叉编译环境变量错误、依赖库缺失及Go依赖管理不规范。可通过Docker容器编译、选择兼容Go版本、正确设置GOOS GOARCH环境变量、安装对应开发包及使用GoModules解决。

CentOS中Fortran与Python如何协同工作从入门到实战完整教程
编程语言 · 2026-07-01

CentOS中Fortran与Python如何协同工作从入门到实战完整教程

在CentOS中,Fortran与Python可通过f2py、SWIG、共享库调用或subprocess协同。f2py封装Fortran为Python模块,支持数组运算;共享库需手动对齐数据类型;系统调用适合独立计算。

CentOS中Golang打包优化方法
编程语言 · 2026-07-01

CentOS中Golang打包优化方法

在CentOS中优化Golang编译打包,可显著提升编译速度并减小二进制文件体积。关键技巧包括:设置环境变量、使用Go模块管理依赖、编译时添加-ldflags= "-s-w "去除调试信息、利用UPX工具压缩、运行strip清理符号表,以及优化cgo内C代码的编译选项。综合运用这些方法能有效优化最终程序。

在CentOS系统中cpustat与其他工具协同使用的完整方法
编程语言 · 2026-07-01

在CentOS系统中cpustat与其他工具协同使用的完整方法

cpustat作为sysstat包的CPU监控工具,可通过管道与grep等命令配合过滤数据,利用脚本自动记录带时间戳的日志,或结合图形工具查看,也可格式化输出后接入Zabbix、Grafana等Web监控系统,实现可视化与告警。

CentOS中readdir与其他Linux发行版的差异
编程语言 · 2026-07-01

CentOS中readdir与其他Linux发行版的差异

CentOS基于RHEL,与Ubuntu、Debian、Fedora在包管理器(yum dnfvsapt)、默认文件系统(XFSvsext4)等存在差异,但readdir等系统调用遵循POSIX标准,行为一致。