我有两个复杂的对象,例如 Object1 和 Object2 。 它们具有大约5个级别的子对象。
Object1
Object2
我需要最快的方法来说明它们是否相同。
在C#4.0中怎么做?
在所有自定义类型上实现IEquatable<T>(通常与覆盖继承Object.Equals和Object.GetHashCode方法一起)。对于复合类型,请在包含的类型Equals内调用包含的类型的方法。对于包含的集合,请使用SequenceEqual扩展方法,该方法在内部IEquatable<T>.Equals或Object.Equals在每个元素上调用。显然,这种方法将需要您扩展类型的定义,但其结果比任何涉及序列化的通用解决方案都快。
IEquatable<T>
Object.Equals
Object.GetHashCode
Equals
SequenceEqual
IEquatable<T>.Equals
编辑 :这是三个层次的嵌套的人为的示例。
对于值类型,通常可以只调用它们的Equals方法。即使从未显式分配字段或属性,它们仍将具有默认值。
对于引用类型,应首先调用ReferenceEquals,以检查引用是否相等–当您碰巧引用同一对象时,这可以提高效率。它还将处理两个引用均为空的情况。如果该检查失败,请确认您实例的字段或属性不为null(以避免NullReferenceException),然后调用其Equals方法。由于我们的成员类型正确,因此IEquatable<T>.Equals将直接调用该方法,而绕过重写的Object.Equals方法(由于强制类型转换,其执行速度会稍慢)。
ReferenceEquals
NullReferenceException
覆盖时Object.Equals,还应该覆盖Object.GetHashCode; 为了简洁起见,我在下面没有这样做。
public class Person : IEquatable<Person> { public int Age { get; set; } public string FirstName { get; set; } public Address Address { get; set; } public override bool Equals(object obj) { return this.Equals(obj as Person); } public bool Equals(Person other) { if (other == null) return false; return this.Age.Equals(other.Age) && ( object.ReferenceEquals(this.FirstName, other.FirstName) || this.FirstName != null && this.FirstName.Equals(other.FirstName) ) && ( object.ReferenceEquals(this.Address, other.Address) || this.Address != null && this.Address.Equals(other.Address) ); } } public class Address : IEquatable<Address> { public int HouseNo { get; set; } public string Street { get; set; } public City City { get; set; } public override bool Equals(object obj) { return this.Equals(obj as Address); } public bool Equals(Address other) { if (other == null) return false; return this.HouseNo.Equals(other.HouseNo) && ( object.ReferenceEquals(this.Street, other.Street) || this.Street != null && this.Street.Equals(other.Street) ) && ( object.ReferenceEquals(this.City, other.City) || this.City != null && this.City.Equals(other.City) ); } } public class City : IEquatable<City> { public string Name { get; set; } public override bool Equals(object obj) { return this.Equals(obj as City); } public bool Equals(City other) { if (other == null) return false; return object.ReferenceEquals(this.Name, other.Name) || this.Name != null && this.Name.Equals(other.Name); } }
更新 :这个答案是几年前写的。从那时起,我开始不再IEquality<T>为此类情况实现可变类型的实现了。平等有两个概念: 同一性 和对 等性 。在内存表示级别上,通常将它们区分为“引用相等”和“值相等”(请参阅“ 相等比较”)。但是,相同的区别也可以应用于域级别。假设您的Person班级拥有一个PersonId属性,该属性对于每个实际的人都是唯一的。具有相同PersonId但不同Age值的两个对象是否应视为相等或不同?上面的答案假设一个在等价之后。但是,IEquality<T>接口(例如集合),它们假定此类实现提供了 identity 。例如,如果要填充HashSet<T>,通常会期望TryGetValue(T,T)调用返回仅共享参数标识的现有元素,而不必返回内容完全相同的等效元素。该概念由以下注释强制执行GetHashCode:
IEquality<T>
Person
PersonId
Age
HashSet<T>
TryGetValue(T,T)
GetHashCode
通常,对于可变引用类型,GetHashCode()仅在以下情况下才应覆盖: 您可以从不可变的字段中计算哈希码;要么 您可以确保在对象包含在依赖于其哈希代码的集合中时,该可变对象的哈希代码不会更改。
通常,对于可变引用类型,GetHashCode()仅在以下情况下才应覆盖:
GetHashCode()