LINQ - Concat、Union、Intersect、Except
在 C# LINQ 中,Concat、Union、Intersect、Except 是处理集合合并/筛选的核心方法,覆盖拼接、去重合并、交集、差集四大核心场景。结合你提供的宠物(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>决定,后者更灵活。

浙公网安备 33010602011771号