当前位置:首页 > 技术 > 正文内容

C# 使用 LINQ 进行集合运算与引用类型去重策略

访客 技术 2026年10月3日 1

基础值类型的集合运算

C# 标准库提供了便捷的 LINQ 扩展方法处理常见集合操作。对于整型等值类型,可直接调用对应方法获取结果:

var sourceValues = new[] { 10, 20, 30, 40, 50 };
var targetValues = new[] { 20, 40, 50, 60, 70, 80 };

// 提取两集公共元素
var intersection = sourceValues.Intersect(targetValues).ToList(); 

// 源集存在而目标集不存在的元素
var diffFromSource = sourceValues.Except(targetValues).ToList(); 

// 目标集存在而源集不存在的元素
var diffFromTarget = targetValues.Except(sourceValues).ToList(); 

// 合并集合并自动剔除重复值
var combinedSet = sourceValues.Union(targetValues).ToList(); 

引用类型对象的集合处理

当集合元素为自定义类时,直接调用上述方法会默认基于引用地址进行比较,通常不符合业务需求。此时需传入实现了 IEqualityComparer<T> 接口的实例:

public class ProductItem 
{
    public string ItemName { get; set; }
    public int StockId { get; set; }
}

var warehouseA = new[] 
{
    new ProductItem { ItemName = "Laptop", StockId = 501 },
    new ProductItem { ItemName = "Monitor", StockId = 502 }
};

var warehouseB = new[] 
{
    new ProductItem { ItemName = "Laptop", StockId = 501 },
    new ProductItem { ItemName = "Speaker", StockId = 503 }
};

// 执行集合操作时需传递比较器实例
var mergedInventory = warehouseA.Union(warehouseB, new ProductComparer()).ToList();
var sharedStock = warehouseA.Intersect(warehouseB, new ProductComparer()).ToList();

实现自定义比较器

重写 IEqualityComparer<T> 必须同步实现 Equals 与 GetHashCode,以维持哈希表容器的查询效率与逻辑一致性。

单字段匹配

若仅依赖单一属性(如数据库主键)判定对象等同:

public class ProductComparer : IEqualityComparer<ProductItem>
{
    public bool Equals(ProductItem x, ProductItem y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x is null || y is null) return false;
        return x.StockId == y.StockId;
    }

    public int GetHashCode(ProductItem obj)
    {
        if (obj is null) return 0;
        return obj.StockId.GetHashCode();
    }
}

复合条件匹配

当多个属性共同构成业务唯一键时,需将各字段哈希值进行组合:

public class CompositeProductComparer : IEqualityComparer<ProductItem>
{
    public bool Equals(ProductItem x, ProductItem y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x is null || y is null) return false;
        return x.ItemName == y.ItemName && x.StockId == y.StockId;
    }

    public int GetHashCode(ProductItem obj)
    {
        if (obj is null) return 0;
        
        int nameHash = obj.ItemName?.GetHashCode() ?? 0;
        int idHash = obj.StockId.GetHashCode();
        
        // 异或运算组合哈希值
        return nameHash ^ idHash;
    }
}

泛型工具类简化开发

高频手写比较器类易产生冗余代码。针对单字段场景,可封装通用比较器并配合扩展方法提升语法整洁度:

public class PropertyComparer<T, TProp> : IEqualityComparer<T>
{
    private readonly Func<T, TProp> _keySelector;
    
    public PropertyComparer(Func<T, TProp> selector) => _keySelector = selector;

    public bool Equals(T x, T y) => EqualityComparer<TProp>.Default.Equals(_keySelector(x), _keySelector(y));
    
    public int GetHashCode(T obj) => _keySelector(obj) is null ? 0 : _keySelector(obj).GetHashCode();
}

public static class EnumerableExtensions
{
    /// <summary>
    /// 依据指定属性投影执行去重
    /// </summary>
    public static IEnumerable<T> UniqueBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> keySelector)
    {
        return source.Distinct(new PropertyComparer<T, TProp>(keySelector));
    }
}

调用方式显著简化:

var distinctWarehouseA = warehouseA.UniqueBy(p => p.ItemName).ToList();

多字段泛型方案说明

若需支持类似 s => s.Id && s.Name 的多条件去重,可将目标属性封装为匿名对象或元组作为委托返回值,再次复用上述泛型比较器即可。LINQ 交集、并集、差集与去重操作的底层核心机制统一依赖相等比较器接口。实际工程中应结合 Microsoft 官方 API 文档查阅边界行为与时间复杂度特性,部分关联查询场景可替代性使用 Join 实现数据对齐。

标签: C#

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。