前言

IEquatable<T>IEqualityComparer<T>C# 中用于比较对象的接口,它们有以下区别:

  • IEquatable

    IEquatable<T> 是一个泛型接口,定义了一个用于比较对象相等性的方法 Equals(T other)

    当你想要在类中自定义相等性比较的逻辑时,可以实现 IEquatable<T> 接口。

    实现了 IEquatable<T> 接口的类可以通过调用 Equals 方法来进行对象之间的相等性比较。
  • IEqualityComparer

    IEqualityComparer<T> 也是一个泛型接口,定义了两个方法:Equals(T x, T y)GetHashCode(T obj)

    当你需要自定义对象之间的相等性比较逻辑和哈希码计算逻辑时,可以实现 IEqualityComparer<T> 接口。

通常场景他们使用效果是一样的,例如进行Enumerable.Distinct

IEquatable<T>

IEquatable<T>C# 中用于比较对象相等性的泛型接口。它定义了一个名为 Equals 的方法,用于比较对象与另一个对象的相等性。

通过实现 IEquatable<T> 接口,你可以在类中自定义对象相等性的比较逻辑。这使得你的类更具灵活性,可以根据你的需求来确定两个对象是否相等。

根据MS推荐实现了Equals最好也实现他的操作符==!=

using System;
using System.Collections.Generic;
using System.Xml.Linq; static class Example
{
static void Main()
{
List<Person> personList = new List<Person>()
{
new Person() { Name = "A", Age = 1, },
new Person() { Name = "B", Age = 2, },
new Person() { Name = "B", Age = 2, },
new Person() { Name = "C", Age = 3, },
new Person() { Name = "C", Age = 3, },
}; IEnumerable<Person> nameDisList = personList.Distinct(); // 实现了 IEquatable 按照 Name + Age 进行去重 // {Name=A, Age=1},{Name=B, Age=2},{Name=C, Age=3}
Console.WriteLine(string.Join(",", nameDisList)); Console.ReadKey();
}
} public class Person : IEquatable<Person>
{
public int Age
{
get;
set;
} public string Name
{
get;
set;
} public override bool Equals(object obj)
{
return this.Equals(obj as Person); // 调用内部的对比即可
} public bool Equals(Person other)
{
if (other == null)
return false; // Optimization for a common success case.
if (ReferenceEquals(this, other))
return true; if (this.Age == other.Age && this.Name == other.Name)
return true;
else
return false;
} public override int GetHashCode()
{
// 选择两个不同的质数,例如 17 和 23
// 关于质数,可以参考 https://en.wikipedia.org/wiki/Prime_number
// 重写 GetHashCode() 方法最佳实践请见 https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-overriding-gethashcode
int hashCode = 17;
hashCode = hashCode * 23 + EqualityComparer<string>.Default.GetHashCode(this.Name);
hashCode = hashCode * 23 + EqualityComparer<int>.Default.GetHashCode(this.Age); return hashCode;
} public static bool operator ==(Person p1, Person p2)
{
if (p1 is null && p2 is null) // 一定不要操作符重写中使用 == 操作符来判断两个对象是否相等,否则会堆栈溢出
return true; if (((object)p1) == null || ((object)p2) == null)
return Equals(p1, p2); return p1.Equals(p2);
} public static bool operator !=(Person p1, Person p2)
{
return !(p1 == p2); // 使用不等于即可
}
}

IEqualityComparer<T>

IEqualityComparer<T>C# 中用于比较对象相等性的泛型接口。它定义了两个方法:Equals(object x, object y)GetHashCode(object obj)

Equals(object x, object y) 方法用于比较两个对象 xy 是否相等。它返回一个布尔值,指示两个对象是否相等。

GetHashCode(object obj) 方法用于计算对象 obj 的哈希码。哈希码是一个整数,用于在哈希表等数据结构中进行快速查找和比较。

通常情况下,IEqualityComparer 接口用于在没有泛型的情况下,提供自定义的对象相等性比较和哈希码计算逻辑。 你可以实现 IEqualityComparer 接口来定义你自己的比较器,以便在需要自定义对象相等性比较的情况下使用。

using System;
using System.Collections.Generic;
using System.Xml.Linq; static class Example
{
static void Main()
{
List<Person> personList = new List<Person>()
{
new Person() { Name = "A", Age = 1, },
new Person() { Name = "B", Age = 2, },
new Person() { Name = "B", Age = 3, },
new Person() { Name = "C", Age = 4, },
new Person() { Name = "B", Age = 4, },
}; IEnumerable<Person> nameDisList = personList.Distinct(new PersonNameEqualityComparer());
IEnumerable<Person> ageDisList = personList.Distinct(new PersonAgeEqualityComparer()); // {Name=A, Age=1},{Name=B, Age=2},{Name=C, Age=4}
Console.WriteLine(string.Join(",", nameDisList));
// {Name = A, Age = 1},{ Name = B, Age = 2},{ Name = B, Age = 3},{ Name = C, Age = 4}
Console.WriteLine(string.Join(",", ageDisList)); Console.ReadKey();
}
} class PersonNameEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person p1, Person p2)
{
if (ReferenceEquals(p1, p2))
return true;
if (p2 is null || p1 is null)
return false; return p1.Name == p2.Name;
} public int GetHashCode(Person p) => p.Name.GetHashCode();
} class PersonAgeEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person p1, Person p2)
{
if (ReferenceEquals(p1, p2))
return true;
if (p2 is null || p1 is null)
return false; return p1.Age == p2.Age;
} public int GetHashCode(Person p) => p.Age.GetHashCode();
}

如果是单纯的去重,高版本的C#可以使用Enumerable.DistinctBy

后言

通过上面介绍我们了解了它们两种的使用方式,那么什么时候用IEqualityComparer<T> 什么时候用IEquatable<T>呢?

  1. 当目标类你没有源代码,无法直接修改的时候请使用IEqualityComparer<T>
  2. 当目标类你只需要一种对比方式可以考虑使用IEquatable<T>
  3. 当目标类有多重对比方式,例如根据AB或者BC等多重属性进行对比,你可以定义多个IEqualityComparer<T>
  4. 当然两种方式可以共存,根据需要使用

参考

C#中对比两个对象是否相等最佳实践,IEquatable和IEqualityComparer的差异的更多相关文章

  1. Java中对比两个对象中属性值[反射、注解]

    在Java中通常要比较两个对象在修改前与修改后的值是否相同,一般我们采用的是反射技术获取对象的get方法[或其他的方法]获取值并做比较.如果系统将修改的属性名称也显示出来,这样就能更直观的显示类中的哪 ...

  2. python中判断两个对象是否相等

    #coding=utf-8#比较两个对象是否相等#python 2中使用cmp(),==,is#is 主要是判断 2 个变量是否引用的是同一个对象,如果是的话,则返回 true,否则返回 false. ...

  3. Java 中判断两个对象是否相等

    由于每次实例化一个对象时,系统会分配一块内存地址给这个对象,而系统默认是根据内存地址来检测是否是同一个对象,所以就算是同一个类里实例化出来的对象它们也不会相等. public class Transp ...

  4. java中new两个对象,在堆中开辟几个对象空间

    内存堆中有两个对象,两个对象里都有独立的变量.p1 p2指向的不是同一个内存空间. 也可以这样描述引用p1,p2指向两个不同的对象.

  5. 定义一个类Point,代表一个点,public属性有x和y,方法有显示点坐标 show(),构造函数有两个参数分别给x,y赋值,在main方法中构造两个对象,再创建一方法(getMiddle)为取两个点构成线段的中点的坐标,参数为2个点对象,调用此方法后得到一个新的点,编写Application,显示该对象的坐标值。

    这个题让我更加明白了类创建对象的实质 代码中用到:1.对象作形参;2.对象作返回值 以下用代码具体分析: class Point1{ public double x; public double y; ...

  6. 工作这么多年,我总结的数据传输对象 (DTO) 的最佳实践

    前言 数据传输对象 (DTO) 是一种设计模式,常用于软件开发不同层或者不同系统之间传输数据.DTO 的主要目的是封装数据并防止它被其他层或系统直接访问或修改.通过遵循一组最佳实践,开发人员可以确保他 ...

  7. java中拼接两个对象集合

    目标:  根据两个list中每条记录的某个属性是否相同来拼接. 1.首先定义一个字符串 String str = "[{\"ITEMID\":2,\"ITEMN ...

  8. 2.编写实现:有一个三角形类Triangle,成员变量有底边x和另一条边y,和两边的夹角a(0<a<180),a为静态成员,成员方法有两个:求面积s(无参数)和修改角度(参数为角度)。 编写实现: 构造函数为 Triangle(int xx,int yy,int aa) 参数分别为x,y,a赋值 在main方法中构造两个对象,求出其面积,然后使用修改角度的方法,修改两边的夹角,再求出面积值。(提示

    求高的方法 h=y*Math.sin(a) 按题目要求,需要我们做的分别是:1.改角度2.显示角度3.求面积并显示 代码用到:1.静态成员变量以修改角度2.数学函数 以下具体代码具体分析 import ...

  9. Delphi中比较两个对象是否一致及地址是否相同[转]

    在delphi中,C#也是如此,对象的地址与对象变量(引用)的地址不是同一个概念.要加以区别. procedure TForm1.btn1Click(Sender: TObject); var    ...

  10. javascript中对两个对象进行排序 和 java中的两个对象排序

    javascript中的对象数组排序 一 定义一个对象数组 var text = [{"name":"张","age":24},{" ...

随机推荐

  1. Fireboom on Sealos:半小时搞定一个月的接口工作

    后端日常开发工作中有 88% 的接口都是 CURD,占用了超过 6 成开发时间.这些工作枯燥乏味,且价值低下,不仅荒废了时间,还无法获得任何成就感.而 Fireboom 可在 2 分钟内,完成传统模式 ...

  2. C++类内存分布+ Studio工具

    书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承.虚函数存在的情况下. 工欲善其事,必先利其器,我们先用好Visual Stu ...

  3. Python并发编程——multiprocessing模块、Process类、Process类的使用、守护进程、进程同步(锁)、队列、管道、共享数据 、信号量、事件、 进程池

    文章目录 一 multiprocessing模块介绍 二 Process类的介绍 三 Process类的使用 四 守护进程 五 进程同步(锁) 六 队列(推荐使用) 七 管道 八 共享数据 九 信号量 ...

  4. html表单与框架

    1.以form开头 其中常用的属性有 action=""  method=""  enctype=""   name="" ...

  5. 强化学习的一周「GitHub 热点速览」

    当强化学习遇上游戏,会擦出什么样的火花呢?PokemonRedExperiments 将经典的 Pokeman 游戏接上了强化学习,效果非同凡响,不然能一周获得 4.5k star 么?看看效果图就知 ...

  6. 字符串小记 II:字符串自动机

    OI 中的自动机指的是"有限状态自动机",它是对一串信号进行处理的数学模型,一般由以下三部分构成: 字符集(\(\Sigma\)),能够输入进自动机的字符集合. 状态集合(\(Q\ ...

  7. Util应用框架基础(一) - 依赖注入

    本节介绍Util应用框架依赖注入的使用和配置扩展. 文章分为多个小节,如果对设计原理不感兴趣,只需阅读基础用法部分即可. 概述 当你想调用某个服务的方法完成特定功能时,首先需要得到这个服务的实例. 最 ...

  8. AJAX入门实例

    1.什么是 AJAX ? AJAX = 异步 JavaScript 和 XML. AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这 ...

  9. 解决IDEA中.properties文件中文变问号(???)的问题(已解决)

    问题背景 构建SpringBoot项目时,项目结构中有一个application.properties文件.这个项目是Spring Boot一个特有的配置文件.内容如下(我写了一些日志的配置): 写到 ...

  10. Ansible自动化部署工具-role模式安装filebeat实际案例分析

    大家好,我是蓝胖子,前面一节我简单的讲了讲Ansible的架构和编排任务的语法,可以发现,通过playbook方式编排任务时,能够将任务文档化,但是在面对比较复杂且不同业务的任务编排时,维护playb ...