如何在包含子集合的类中正确重写Equals方法
如何正确实现包含集合属性的类的相等性检查
你定义了Person类和Address类,重写了它们的Equals和GetHashCode方法,但比较两个属性完全相同的Person实例时,相等性检查返回false。
你的原始代码如下:
原始类定义
public class Person { public string Name { get; set; } public List<Address> Addresses { get; set; } public override bool Equals(object? obj) { return obj is Person person && Name == person.Name && EqualityComparer<List<Address>>.Default.Equals(Addresses, person.Addresses); } public override int GetHashCode() { return HashCode.Combine(Name, Addresses); } } public class Address { public string StreetName { get; set; } public override bool Equals(object? obj) { return obj is Address address && StreetName == address.StreetName; } public override int GetHashCode() { return HashCode.Combine(StreetName); } }
测试代码
var person = new Person() { Name = "Mr Test", Addresses = new List<Address>() { new Address() { StreetName = "Test Street" } } }; var person2 = new Person() { Name = "Mr Test", Addresses = new List<Address>() { new Address() { StreetName = "Test Street" } } }; var result = person.Equals(person2); // 此处result为false
问题原因
问题出在List<Address>的比较逻辑上:List<T>的默认相等性比较(EqualityComparer<List<Address>>.Default.Equals)是引用比较——它只会检查两个List是否指向同一个内存对象,不会逐个对比集合内元素的内容是否相等。即使你已经正确重写了Address的Equals方法,默认List比较也不会调用它来判断元素相等性。
解决方案
要正确比较集合内的元素,需要使用SequenceEqual方法(来自System.Linq命名空间),它会逐个遍历两个序列的元素,并调用元素类型的Equals方法判断单个元素是否相等。同时要处理集合为null的边界情况,避免空引用异常。
修改后的完整代码如下:
using System.Linq; public class Person { public string Name { get; set; } public List<Address> Addresses { get; set; } public override bool Equals(object? obj) { // 同一引用直接返回相等 if (ReferenceEquals(this, obj)) return true; // 类型不符或为null返回不等 if (obj is not Person person) return false; // 比较Name属性 if (Name != person.Name) return false; // 处理Addresses为null的情况 if (Addresses == null && person.Addresses == null) return true; if (Addresses == null || person.Addresses == null) return false; // 逐个比较集合内的Address元素 return Addresses.SequenceEqual(person.Addresses); } public override int GetHashCode() { // 先计算Name的哈希值 var hash = Name?.GetHashCode() ?? 0; // 累加每个Address的哈希值到总哈希中 if (Addresses != null) { foreach (var address in Addresses) { hash = HashCode.Combine(hash, address?.GetHashCode() ?? 0); } } return hash; } } public class Address { public string StreetName { get; set; } public override bool Equals(object? obj) { if (ReferenceEquals(this, obj)) return true; if (obj is not Address address) return false; return StreetName == address.StreetName; } public override int GetHashCode() { return StreetName?.GetHashCode() ?? 0; } }
修改说明
- Person.Equals方法:增加了引用相等判断、null值处理,改用
SequenceEqual实现集合元素的内容比较。 - Person.GetHashCode方法:不再直接使用List的引用哈希,而是基于
Name和每个Address的哈希值组合生成,保证内容相同的对象哈希值一致,符合GetHashCode的规范。
修改后运行原测试代码,result会返回true。
内容的提问来源于stack exchange,提问作者Mark D
相关产品推荐
相关产品推荐

