intersect() 返回空集合是因为自定义类型未重写equals()和gethashcode(),导致引用比较失效;需实现iequatable或传iequalitycomparer;distinctby()(.net 6+)按指定键去重更灵活。

直接用 Intersect() 拿交集,Distinct() 去重,但自定义类型不重写 Equals() 和 GetHashCode() 就会出空结果——这是最常踩的坑。
为什么 Intersect() 返回空集合?
不是数据没交集,而是比较逻辑失效了。默认情况下,Intersect() 对引用类型(比如 Student 类实例)做的是引用相等判断,两个内容完全相同的对象只要不是同一个实例,就判为“不相等”。
- 基础类型(
int、string、DateTime)可直接用,无需额外处理 - 自定义类必须实现
IEquatable<t></t>,或至少重载Equals()+GetHashCode() - 若只想按某字段比较(如只看
Id),传入IEqualityComparer<t></t>实现类,比改模型更轻量
Distinct() 和 DistinctBy() 该怎么选?
Distinct() 是全字段比较去重,DistinctBy()(.NET 6+)允许你指定一个键来决定“重复”的标准,更灵活也更常用。
-
Distinct():适用于值类型或已正确实现相等逻辑的引用类型 -
DistinctBy(s => s.Name):按姓名去重,保留第一个出现的Student;即使Id不同,只要Name相同就算重复 -
DistinctBy(s => s.Id):按 ID 去重,适合 ID 唯一但其他字段可能重复的场景 - 注意:
DistinctBy()不支持 .NET 5 及更早版本,别在旧项目里硬套
交集/差集/合并操作中容易忽略的顺序和去重行为
这些方法表面看着像集合运算,但实际执行时有隐含规则,不注意会导致结果不符合预期。
-
Intersect()结果顺序 = 第一个集合中首次出现的顺序,且自动去重(哪怕原集合有多个相同交集元素,结果只留一个) -
Except()保留第一个集合的顺序,也自动去重;names1.Except(names2)表示“names1里有、names2里没有”的元素 -
Union()合并后去重,顺序是先list1再list2中未出现过的元素;Concat()则完全拼接,不做任何去重 - 所有这些方法返回的都是新序列(
IEnumerable<t></t>),原集合不受影响
性能关键点:别在循环里调 Contains() 做交集
手写双重循环或对每个元素调 list2.Contains(item),时间复杂度是 O(n×m),数据量稍大就明显卡顿。而 Intersect() 底层用 HashSet<t></t> 构建索引,实际是 O(n+m)。
- 小数据量(Intersect()
- 大数据量下,自己用
HashSet<t></t>预构建查找表,再遍历另一个集合,和Intersect()效果一致 - 字符串比较要注意大小写:默认区分大小写,需要忽略时传
StringComparer.OrdinalIgnoreCase
真正麻烦的从来不是语法怎么写,而是“为什么没结果”——多数时候是相等比较器没生效,或者误以为 Intersect() 会保留重复项。盯住 Equals() 和 GetHashCode() 这两个方法,比查文档快得多。










