LINQ - Concat、Union、Intersect、Except

在 C# LINQ 中,ConcatUnionIntersectExcept 是处理集合合并/筛选的核心方法,覆盖拼接、去重合并、交集、差集四大核心场景。结合你提供的宠物(pets)/宠物主人(people)代码示例,以下是清晰的对比、用法和实操演示:

一、四大集合方法总览

先明确每个方法的核心行为,以及相等性比较在其中的角色

方法 核心作用 是否依赖“相等性比较” 核心特点
Concat 拼接两个集合(A + B),保留所有元素 ❌ 不依赖 简单拼接,重复元素会保留
Union 合并两个集合并去重(A ∪ B) ✅ 依赖 合并后仅保留唯一元素
Intersect 取两个集合的交集(同时存在于A和B的元素) ✅ 依赖 只保留“共有元素”
Except 取两个集合的差集(A有、B没有的元素) ✅ 依赖 只保留“A独有的元素”

二、逐个详解(结合宠物示例 + 相等性比较)

基于你提供的 pets(8只宠物)和 people(关联前6只),结合默认比较自定义 IEqualityComparer<T> 演示:

1. Concat(拼接)

  • 行为:直接将集合B追加到集合A后,不做任何相等性判断,保留所有元素(包括重复、不同实例但值相同的元素)。
  • 示例
    var cats = pets.Where(p => p.Type == PetType.Cat); // ID:2、3、7、8
    var dogs = pets.Where(p => p.Type == PetType.Dog); // ID:4、5、6
    var concatResult = cats.Concat(dogs); 
    // 结果:2、3、7、8、4、5、6(共7个元素,无去重)
    

2. Union(合并去重)

  • 行为:合并A和B,自动去重(判定“重复”的核心是相等性比较)。

  • 相等性规则的影响

    • 默认:依赖 Pet 自身的 IEquatable<T> 或引用地址;
    • 自定义:通过 IEqualityComparer<T> 指定规则。
  • 示例

    // 被主人关联的猫(ID:2、3)
    var ownedCats = people.SelectMany(p => p.Pets).Where(p => p.Type == PetType.Cat);
    // 所有猫(ID:2、3、7、8)
    var allCats = pets.Where(p => p.Type == PetType.Cat);
    
    // ① 默认比较(按Pet的IEquatable<T>,这里是按ID)
    var unionDefault = ownedCats.Union(allCats); 
    // 结果:2、3、7、8(去重,保留唯一ID)
    
    // ② 自定义比较器(按“宠物名称”去重)
    var unionWithComparer = ownedCats.Union(allCats, new PetNameEqualityComparer());
    // 结果:按名称去重(若有名称重复的猫,仅保留一个)
    

3. Intersect(交集)

  • 行为:保留同时存在于A和B的元素,判定“存在”的核心是相等性比较
  • 示例
    var allCats = pets.Where(p => p.Type == PetType.Cat); // 所有猫
    var ownedPets = people.SelectMany(p => p.Pets); // 被主人关联的宠物
    
    // ① 默认比较(按ID)
    var intersectDefault = allCats.Intersect(ownedPets);
    // 结果:ID=2、3的猫(这两只猫同时在“所有猫”和“被关联宠物”中)
    
    // ② 自定义比较器(按“类型+重量”)
    var intersectWithComparer = allCats.Intersect(ownedPets, new PetTypeWeightComparer());
    // 结果:类型为Cat、且重量与被关联宠物一致的元素
    

4. Except(差集)

  • 行为:保留“仅在A中、不在B中”的元素,判定“存在”的核心是相等性比较
  • 示例
    var allCats = pets.Where(p => p.Type == PetType.Cat); // 所有猫
    var ownedPets = people.SelectMany(p => p.Pets); // 被主人关联的宠物
    
    // ① 默认比较(按ID)
    var exceptDefault = allCats.Except(ownedPets);
    // 结果:ID=7、8的猫(这两只猫在“所有猫”中,但不在“被关联宠物”中)
    
    // ② 自定义比较器(按“名称”)
    var exceptWithComparer = allCats.Except(ownedPets, new PetNameEqualityComparer());
    // 结果:名称不在“被关联宠物”中的猫
    

三、相等性比较的核心:默认 vs 自定义(IEqualityComparer

Union/Intersect/Except 的行为完全由相等性规则决定,而规则的选择分两种场景:

场景 实现方式 适用情况
默认比较 实体类实现 IEquatable<T>,或重写 Equals/GetHashCode 规则固定、可修改实体类源码
自定义比较 实现 IEqualityComparer<T>,传入LINQ方法重载 规则灵活、不可修改实体类、多规则切换

四、快速记忆

  • Concat:拼集合,不查重,无比较;
  • Union:合并后去重,相等则去重
  • Intersect:找共有,相等才保留
  • Except:找独有,A有B无(相等则排除)
  • 相等性规则由 IEquatable<T>IEqualityComparer<T> 决定,后者更灵活。
posted @ 2025-12-25 14:24  【唐】三三  阅读(22)  评论(0)    收藏  举报