如何在含数组属性的C# Look类数组中查找重复项?
查找包含数组属性的Look类重复项
因为数组是引用类型,默认的相等比较会检查引用地址而非内容,所以直接用匿名类型分组无法正确识别ClothesIds内容相同的重复项。下面提供几种可行的解决方案:
方案一:将数组转换为可哈希的字符串(简单场景首选)
把ClothesIds数组拼接成有序字符串(排序是为了让元素相同但顺序不同的数组被视为相等,若需要严格顺序相同则去掉排序),作为分组键的一部分:
var duplicateLooks = looks.GroupBy(x => new { x.HairId, x.MakeupId, ClothesKey = string.Join(",", x.ClothesIds?.OrderBy(id => id) ?? Array.Empty<int>()) }) .Where(g => g.Count() > 1) .Select(g => g.Key);
注意:如果数组元素包含分隔符(比如逗号),需要换用不会冲突的分隔符,或者对元素进行转义处理。
方案二:自定义相等比较器(无需修改原类)
实现IEqualityComparer<Look>,自定义属性和数组的比较逻辑,然后传给GroupBy的重载方法:
自定义比较器类
public class LookEqualityComparer : IEqualityComparer<Look> { public bool Equals(Look x, Look y) { if (ReferenceEquals(x, y)) return true; if (x is null || y is null) return false; // 比较基础属性 if (x.HairId != y.HairId || x.MakeupId != y.MakeupId) return false; // 比较数组内容 if (x.ClothesIds == null && y.ClothesIds == null) return true; if (x.ClothesIds == null || y.ClothesIds == null) return false; if (x.ClothesIds.Length != y.ClothesIds.Length) return false; // 选择1:顺序相关的数组比较(严格按元素顺序判断相等) for (int i = 0; i < x.ClothesIds.Length; i++) { if (x.ClothesIds[i] != y.ClothesIds[i]) return false; } // 选择2:顺序无关的数组比较(只要元素相同就视为相等) // return new HashSet<int>(x.ClothesIds).SetEquals(y.ClothesIds); return true; } public int GetHashCode(Look obj) { if (obj is null) return 0; int hash = HashCode.Combine(obj.HairId, obj.MakeupId); if (obj.ClothesIds != null) { // 对应顺序相关的哈希计算 foreach (int id in obj.ClothesIds) { hash = HashCode.Combine(hash, id); } // 对应顺序无关的哈希计算,替换上面的循环 // var sortedIds = obj.ClothesIds.OrderBy(id => id); // foreach (int id in sortedIds) // { // hash = HashCode.Combine(hash, id); // } } return hash; } }
使用比较器分组
var duplicateLooks = looks.GroupBy(look => look, new LookEqualityComparer()) .Where(g => g.Count() > 1) .Select(g => g.Key);
方案三:让Look类实现IEquatable接口(规范方案)
如果可以修改原Look类,直接实现IEquatable<Look>接口,让类自带正确的相等比较逻辑:
public class Look : IEquatable<Look> { public int HairId { get; set; } public int MakeupId { get; set; } public int[] ClothesIds { get; set; } public bool Equals(Look other) { if (other is null) return false; if (ReferenceEquals(this, other)) return true; if (HairId != other.HairId || MakeupId != other.MakeupId) return false; if (ClothesIds == null && other.ClothesIds == null) return true; if (ClothesIds == null || other.ClothesIds == null) return false; if (ClothesIds.Length != other.ClothesIds.Length) return false; // 顺序相关的比较 for (int i = 0; i < ClothesIds.Length; i++) { if (ClothesIds[i] != other.ClothesIds[i]) return false; } // 顺序无关的比较,替换上面的循环 // return new HashSet<int>(ClothesIds).SetEquals(other.ClothesIds); return true; } public override bool Equals(object obj) { return Equals(obj as Look); } public override int GetHashCode() { int hash = HashCode.Combine(HairId, MakeupId); if (ClothesIds != null) { // 顺序相关的哈希计算 foreach (int id in ClothesIds) { hash = HashCode.Combine(hash, id); } // 顺序无关的哈希计算,替换上面的循环 // var sortedIds = ClothesIds.OrderBy(id => id); // foreach (int id in sortedIds) // { // hash = HashCode.Combine(hash, id); // } } return hash; } }
直接分组
此时可以像原来无数组属性时一样使用GroupBy:
var duplicateLooks = looks.GroupBy(x => x) .Where(g => g.Count() > 1) .Select(g => g.Key);
内容的提问来源于stack exchange,提问作者Grigory Zhadko
相关产品推荐
相关产品推荐

