前言

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. SQL函数Intersect,except整理

    1.  集合函数使用的规则 ①   每个集合要求列数量相同,列顺序相同. ②   每个集合显示的列,要求数据类型一致或者可隐式转换成同一数据类型 ③   最终集合列名称与第一个集合的列名称一致  2. ...

  2. 背景图片随机API

    在美化博客园的时候,遇到了一个问题:博客背景图片只支持一张图片,看到有道友说可以用API随机图片. 于是就有了这篇文章. 本文主要整理了一些随机图片API,希望对你有帮助. 岁月小筑 https:// ...

  3. Update 1.82.1: The update addresses this security issue.

    August 2023 (version 1.82) 更新后显示发行说明 Update 1.82.1: The update addresses this security issue. Welcom ...

  4. 《最新出炉》系列初窥篇-Python+Playwright自动化测试-18-处理鼠标拖拽-上篇

    1.简介 本文主要介绍两个在测试过程中可能会用到的功能:在selenium中宏哥介绍了Actions类中的拖拽操作和Actions类中的划取字段操作.例如:需要在一堆log字符中随机划取一段文字,然后 ...

  5. vue项目打包,解决静态资源无法加载和路由加载无效(404)问题

    打包后的项目静态资源无法使用,导致页面空白 静态资源无法使用,那就说明项目打包后,图片和其他静态资源文件相对路径不对,此时找到config里面的index.js,在build模块下加入assetsPu ...

  6. Vue源码学习(十三):nextTick()方法

    好家伙,nextTick, (...这玩意,不太常用) 1.什么是nextTick 在Vue中,nextTick是一个用于异步执行回调函数的方法. 它在Vue更新DOM后被调用,以确保在下一次DOM更 ...

  7. codeforces #865 div1A

    A. Ian and Array Sorting 思路:首先我们可以从前往后做一遍,把除了最后一个元素其他所有数都变成和第一个数一样的数,然后假如前n-1个数个数为偶数,这样我们分组进行操作,一定可以 ...

  8. webservice soap hessian

    http://www.blogjava.net/freeman1984/archive/2010/01/20/310198.html http://www.cnblogs.com/JeffreySun ...

  9. 使用rancher rke快速安装k8s集群

    概述 Rancher Kubernetes Engine(RKE)是一个用于部署.管理和运行Kubernetes集群的开源工具.旨在简化Kubernetes集群的部署和操作. RKE具有以下特点和功能 ...

  10. GPTs破冰硅基文明社会

    GPTs破冰硅基文明社会 渐进是技术革命的常态 技术革命看似一夕之间就颠覆了世界,但实际上每项重大技术进步的背后,都经历了漫长的渐进积累.以蒸汽机为例,最初动力微弱.效率低下,需要大量工程师跟车维护, ...