前言

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. 【知识杂谈#1】Linux如何安装net-tools和sbin配置PATH

    1. Linux下载net-tools 在Linux上下载net-tools包的方法可能会因你所使用的Linux发行版而有所不同.在某些现代的Linux发行版中,net-tools已经被弃用,而推荐使 ...

  2. FFmpeg H.264编码器指南[译]

    H264 视频编码器指南 本指引着眼于x264编码器,这里假设你的FFmpeg 编译了--enable-libx264支持.如果你需要编译支持的帮助请看这篇文档:https://trac.ffmpeg ...

  3. win10系统单独编译和使用WebRTC的回声消除(AEC)、音频增益(AGC)、去噪(NS)模块

    一.简介 本人想单独编译并使用WebRTC的音频回声消除模块,奈何技术有限,于是在百度的海洋里大海捞针,发现了https://www.cnblogs.com/mod109/p/5827918.html ...

  4. centos7 oracle11gR2安装

    CentOS7安装Oracle 11gR2 图文详解 摘自: http://www.linuxidc.com/Linux/2016-04/130559.htm 最近要运维一个项目,准备在家办公,公司无 ...

  5. CF1368B

    题目简化和分析: 因为要求长度最小,所以我们每个字符就应该发挥最大的价值,不会有没有作用的字符. 设有 \(x_1\) 个 \(c\) ,\(x_2\) 个 \(o\) ,\(x_3\) 个 \(d\ ...

  6. CF862B

    题目简化和分析: 这是一道较为经典的二分图染色题. 二分图的基本概念 但这题让我们求得是完全二分图. 什么是完全二分图 \(cnt_{1}\) 表示染成颜色种类为 \(1\) 的个数. \(cnt_{ ...

  7. 记Halo1.5版本迁移Halo2.10.0版本

    原文地址: 记Halo1.5版本迁移Halo2.10.0版本 - Stars-One的杂货小窝 上一篇Window10安装linux子系统及子系统安装1Panel面板 - Stars-One的杂货小窝 ...

  8. Redis 7.0 源码环境搭建与阅读技巧

    天下武功,无坚不摧,唯快不破!我的名字叫 Redis,全称是 Remote Dictionary Server. 有人说,组 CP,除了要了解她外,还要给机会让她了解你. 那么,作为开发工程师的你,是 ...

  9. 在Finalshell中一直弹出弹窗要求输入密码的解决方案

    在finalshell中即使输入正确了密码,仍然要求输入(密码错误), 对CentOS的防火墙也已经关闭,但是仍然提示 这个原因是因为自创的用户名在finalshell中不被认可,需要将用户名更改为r ...

  10. xxl-job安装部署文档

    xxl-job安装部署文档 注意:替换yaml文件中的mysql地址 安装方式 kubectl apply -f xxl-job.yaml -n xxxx 安装配置文件 xxl-job.yaml ## ...