前言

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. IntelliJ IDEA安装中文插件

    1.运行IntelliJ IDEA程序2.点击左上角"File"//文件3.点击下拉的"Settings" //设置4.点击"Plugins" ...

  2. ios ipa apple company 开发者账号申请分享攻略

    ios公司开发者账号申请分享攻略 好不容易终于申请下来了ios 公司开发者账号,真是一路艰辛和漫长啊,特别是对于远在大洋彼岸的大中华国家.以下我就分享一下这一路下来的经验,希望对于那些新手同仁们有所帮 ...

  3. DFS(深度优先搜索)洛谷P1162

    看大佬们dfs一遍就出结果,蒟蒻的我dfs了三遍,当然这题也可以用bfs做,但是dfs不用队列代码短一些. #include <iostream> #include <vector& ...

  4. AGC044C Strange Dance 题解

    在2020年A卷省选day2t2有类似建立trie的技巧. 题目链接 显然是建一棵三叉trie树,代表0/1/2 对这棵trie树,我们需要支持子树交换和全局加1 考虑第一个操作怎么做?直接打个懒标记 ...

  5. DHorse改用fabric8的SDK与k8s集群交互

    现状 在dhorse 1.4.0版本之前,一直使用k8s官方提供的sdk与k8s集群交互,官方sdk的Maven坐标如下: <dependency> <groupId>io.k ...

  6. 记一个 Android 14 适配引发的Android 存储权限问题

    一.bug 背景 项目中有下面这样一段代码,在 Android T 版本运行正常,现在适配到 Android U 上之后,运行时 crash 了.... ... values.put(MediaSto ...

  7. CSS单位px、em、rem及它们之间的换算关系

    作者:WangMin 格言:努力做好自己喜欢的每一件事 国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者的区别与优势是什么?接下来我们就来学习一下吧! 单位px.em.rem分 ...

  8. C语言【进制、原反补码、数据类型】

    C语言[进制.原反补码.数据类型] 1.二进制.八进制.十进制.十六进制在C语言中的使用(赋值及输出)? /** 二进制 赋值时以 0b 或 0B 开头: 输出时没有所属占位符, 十六进制整数输出也比 ...

  9. Codeforces Global Round 13 A-D题题解

    写在前边 链接:Codeforces Global Round 13 \(A,B,C,D\) A. K-th Largest Value 链接:A题链接 题目大意: 有一个字串只由\(0.1\)组成, ...

  10. 使用 LCM LoRA 4 步完成 SDXL 推理

    LCM 模型 通过将原始模型蒸馏为另一个需要更少步数 (4 到 8 步,而不是原来的 25 到 50 步) 的版本以减少用 Stable Diffusion (或 SDXL) 生成图像所需的步数.蒸馏 ...