c# - 比较两个复杂对象的最佳方法

标签 c# .net

我有两个复杂的对象,例如 Object1Object2它们有大约 5 个级别的子对象。

我需要最快的方法来判断它们是否相同。

这在 C# 4.0 中如何完成?

最佳答案

实现 IEquatable<T> (通常与覆盖继承的 Object.Equals Object.GetHashCode 方法结合使用)所有自定义类型。在复合类型的情况下,调用包含的类型'Equals包含类型中的方法。对于包含的集合,使用 SequenceEqual 扩展方法,内部调用 IEquatable<T>.EqualsObject.Equals在每个元素上。这种方法显然需要您扩展类型的定义,但其结果比任何涉及序列化的通用解决方案都要快。

编辑:这是一个具有三层嵌套的人为示例。

对于值类型,您通常可以调用它们的 Equals方法。即使从未显式分配字段或属性,它们仍将具有默认值。

对于引用类型,你应该先调用 ReferenceEquals ,它检查引用相等性——当您碰巧引用同一个对象时,这将提高效率。它还会处理两个引用都为空的情况。如果该检查失败,请确认您的实例的字段或属性不为空(以避免 NullReferenceException )并调用其 Equals方法。由于我们的成员类型正确,IEquatable<T>.Equals方法被直接调用,绕过重写的 Object.Equals方法(由于类型转换,其执行速度会稍微慢一些)。

当您覆盖 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>对于此类场景的可变类型。平等有两个概念:同一性等价。在内存表示级别,这些通常被区分为“引用相等”和“值相等”(参见 Equality Comparisons )。然而,同样的区别也适用于域级别。假设您的 Person类有一个 PersonId属性(property),每个不同的现实世界的人都是独一无二的。两个对象是否应该具有相同的 PersonId但不同Age值被认为是相等的还是不同的?上面的答案假设一个是在等价之后。但是,IEquality<T> 有很多用法。接口(interface),例如集合,假设此类实现提供身份。例如,如果您要填充 HashSet<T> , 你通常会期望一个 TryGetValue(T,T) 调用以返回仅共享参数标识的现有元素,不一定是内容完全相同的等效元素。 GetHashCode 上的注释强制执行此概念:

In general, for mutable reference types, you should override GetHashCode() only if:

  • You can compute the hash code from fields that are not mutable; or
  • You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.

关于c# - 比较两个复杂对象的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10454519/

相关文章:

c# - 使用 CaSTLe windsor 解析 WCF service usingfactorymethod

c# - 使用例如创建模拟对象和匿名对象的混合体最小起订量和 AutoFixture?

c# - WCF rest 入门工具包和用于 REST 服务的 Web 应用程序工具包之间的区别

c# - Oracle CHAR 数据类型不适用于 Entity Framework

c# - 如何在文件不再打开时立即收到通知?

c# - 从 C++ DLL 接收 char ** 到 C# string[]

c# - 手动编码编码的 UI 测试

c# - Microsoft Fakes x64 而不是 x86 和 v4.5

c# - 上下文菜单项

.net - 缺少 pdb 文件