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

2023-12-30

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

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

在 C# 4.0 中如何做到这一点?


实施IEquatable<T> https://msdn.microsoft.com/en-us/library/ms131187%28v=vs.110%29.aspx(通常与覆盖继承的Object.Equals https://msdn.microsoft.com/en-us/library/bsc2ak47%28v=vs.110%29.aspx and Object.GetHashCode https://msdn.microsoft.com/en-us/library/system.object_methods%28v=vs.110%29.aspx方法)在所有自定义类型上。对于复合类型,调用包含的类型Equals包含类型中的方法。对于包含的集合,请使用SequenceEqual https://msdn.microsoft.com/en-us/library/bb348567.aspx扩展方法,内部调用IEquatable<T>.Equals or Object.Equals在每个元素上。这种方法显然需要您扩展类型的定义,但其结果比任何涉及序列化的通用解决方案都要快。

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

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

对于引用类型,您应该首先调用ReferenceEquals https://msdn.microsoft.com/en-us/library/system.object.referenceequals%28v=vs.110%29.aspx,它检查引用相等性——当您碰巧引用同一个对象时,这将提高效率。它还可以处理两个引用都为空的情况。如果该检查失败,请确认您的实例的字段或属性不为空(以避免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);
    }
}

Update: 这个答案是几年前写的。从那时起,我开始放弃实施IEquality<T>对于此类场景的可变类型。平等有两个概念:identity and 等价。在内存表示级别,它们通常被区分为“引用相等”和“值相等”(参见平等比较 https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/equality-comparisons)。然而,同样的区别也适用于域级别。假设你的Person类有一个PersonId财产,对于现实世界中每个不同的人来说都是独一无二的。两个物体是否应该具有相同的PersonId但不同Age值被认为是相同还是不同?上面的答案假设一个是等价的。然而,它的用途有很多IEquality<T>接口,例如集合,假设此类实现提供了identity。例如,如果您要填充HashSet<T>,您通常会期望TryGetValue(T,T) https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetvalue调用返回仅共享参数标识的现有元素,不一定是内容完全相同的等效元素。这个概念是由注释强制执行的GetHashCode https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode#notes-to-inheritors:

一般来说,对于可变引用类型,您应该覆盖GetHashCode()除非:

  • 您可以从不可变的字段计算哈希码;或者
  • 当可变对象包含在依赖于其哈希码的集合中时,您可以确保该对象的哈希码不会更改。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

比较两个复杂对象的最佳方法 的相关文章

  • 如何使用 EPPlus 在单个 Excel 单元格中加载包含逗号的文本

    我正在尝试EPPlus 库 http epplus codeplex com 我被困在这个问题上 我必须在单个单元格中加载文本 但是当该文本包含逗号时 我使用的代码会沿着多个单元格 沿着正确的方向 分割文本 这是我用来加载文本的代码 usi
  • 删除是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 C 编程 free 如何知道要释放多少 https stackoverflow com questions 1518711 c programming how does free know how m
  • CSharpRepl emacs 集成?

    我碰巧知道莫诺CSharpRepl http www mono project com CsharpRepl 是否有 emacs csharp 模式使用它在一个窗口中运行 REPL 并像 python 模式一样在另一个窗口中编译 运行 C
  • 如何制作可启动程序?

    所以 这个问题可能看起来很奇怪 但假设我编译了 int main void int x 3 int y 4 int z x y 是否可以让CPU这样运行 如何 例如 这允许我写入监视器吗 如果我没记错的话 内存中有些地方可以写入要显示的内容
  • C# 5 async/await 线程机制感觉不对?

    为什么让调用线程进入异步方法直到内部 等待 一旦调用异步方法就生成一个线程 这不是更干净吗 这样您就可以确定异步方法会立即返回 您不必担心在异步方法的早期阶段没有做任何昂贵的事情 我倾向于知道某个方法是否要在 我的 线程上执行代码 不管是堵
  • 为什么'enable_if'不能用于禁用这里声明

    include
  • UI 函数在快速事件完成之前触发

    我有一个停靠在 Silverlight 应用程序中的 Web 浏览器框架 有时会在其上弹出全窗口 XAML Silverlight UI 元素 我已经或多或少修复了一个老问题 即 Web 框架的内容似乎与 Silverlight 内容不能很
  • 使用 C# 和 wpf 创建类似 Dock 的应用程序

    我需要创建一个与我们购买笔记本电脑时获得的应用程序类似的应用程序 仅当鼠标指针到达窗口顶部时它才可见 那么我怎样才能使用 C 4 0 来做到这一点呢 http www notebookcheck net uploads pics win2
  • C#6 中的长字符串插值行

    我发现 虽然字符串插值在应用于现有代码库的字符串 Format 调用时非常好 但考虑到通常首选的列限制 字符串对于单行来说很快就会变得太长 特别是当被插值的表达式很复杂时 使用格式字符串 您将获得一个可以拆分为多行的变量列表 var str
  • 在 asp.net MVC 中使用活动目录进行身份验证

    我想使用活动目录对我的 asp net mvc 项目中的用户进行身份验证 在网上冲浪了几个小时后 我没有找到任何对我有用的东西 我已经看到了所有结果 但什么也没有 我尝试按照许多帖子的建议编辑我的 web config 如果有人可以帮助我提
  • 使用具有抗锯齿功能的 C# 更改抗锯齿图像的背景颜色

    我有一个图像需要更改背景颜色 例如 将下面示例图像的背景更改为蓝色 然而 图像是抗锯齿的 所以我不能简单地用不同的颜色替换背景颜色 我尝试过的一种方法是创建第二个图像 仅作为背景 并更改其颜色并将两个图像合并为一个图像 但是这不起作用 因为
  • Linux mremap 不释放旧映射?

    我需要一种方法将页面从一个虚拟地址范围复制到另一个虚拟地址范围 而无需实际复制数据 范围很大 延迟很重要 mremap 可以做到这一点 但问题是它也会删除旧的映射 由于我需要在多线程环境中执行此操作 因此我需要旧映射能够同时使用 因此稍后当
  • 从浏览器访问本地文件?

    您好 我想从浏览器访问系统的本地文件 由于涉及大量安全检查 是否可以通过某种方式实现这一目标 或使用 ActiveX 或 Java Applet 的任何其他工作环境 请帮帮我 要通过浏览器访问本地文件 您可以使用签名的 Java Apple
  • ASP.NET MVC 路由:如何从 URL 中省略“索引”

    我有一个名为 StuffController 的控制器 具有无参数索引操作 我希望从表单中的 URL 调用此操作mysite com stuff 我的控制器定义为 public class StuffController BaseContr
  • CUDA 8 编译错误 -std=gnu++11

    我正在尝试转换一些代码以使用 CUDA 并且我认为我遇到了兼容性问题 我们使用CMake 这些是我使用的 gcc 和 CUDA 版本 gcc version gcc Ubuntu 5 4 0 6ubuntu1 16 04 5 5 4 0 2
  • 需要提取字符串中点后的最后一个数字,如“7.8.9.1.5.1.100”

    我需要提取 C 字符串中最后一个点后面的最后一个数字 例如 7 8 9 1 5 1 100 并将其存储在整数中 Added 该字符串也可以是 7 8 9 1 5 1 1 或 7 8 9 1 5 1 0 我还想验证它在最后一个点之前恰好是 7
  • LINQ 中的“from..where”或“FirstOrDefault”

    传统上 当我尝试从数据库中获取用户的数据时 我使用了以下方法 在某种程度上 DbUsers curUser context DbUsers FirstOrDefault x gt x u LoginName id string name c
  • 来自 3rd 方库的链接器错误 LNK2019

    我正在将旧的 vc 6 0 应用程序移植到 vs2005 我收到以下链接器错误 我花了几天时间试图找到解决方案 错误LNK2019 无法解析的外部符号 imp 创建AwnService 52 在函数 public int thiscall
  • 如何将 SQL“LIKE”与 LINQ to Entities 结合使用?

    我有一个文本框 允许用户指定搜索字符串 包括通配符 例如 Joh Johnson mit ack on 在使用 LINQ to Entities 之前 我有一个存储过程 该存储过程将该字符串作为参数并执行以下操作 SELECT FROM T
  • 为什么匹配模板类上的部分类模板特化与没有模板匹配的另一个部分特化不明确?

    这个问题可能很难用标题中的句子来描述 但这里有一个最小的例子 include

随机推荐