C#中对比两个对象是否相等最佳实践

发布时间 2024-01-04 11:09:22作者: 陈大欠

前言

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

  • IEqualityComparer
    IEqualityComparer<T> 也是一个泛型接口,定义了两个方法:Equals(T x, T y)GetHashCode(T obj)
    当你需要自定义对象之间的相等性比较逻辑和哈希码计算逻辑时,可以实现 IEqualityComparer<T> 接口。
  • IEquatable
    IEquatable<T> 是一个泛型接口,定义了一个用于比较对象相等性的方法 Equals(T other)
    当你想要在类中自定义相等性比较的逻辑时,可以实现 IEquatable<T> 接口。
    实现了 IEquatable<T> 接口的类可以通过调用 Equals 方法来进行对象之间的相等性比较。

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;
static class Example
{
    static void Main()
    {
        List<Person> personList = newList<Person>()
        {
            new Person() { Age = 1, Name = "A" },
            new Person() { Age = 2, Name = "B" },
            new Person() { Age = 3, Name = "C" },
        };
        IEnumerable<Person> nameDisList =personList.Distinct(newPersonNameEqualityComparer());
        IEnumerable<Person> ageDisList =personList.Distinct(newPersonAgeEqualityComparer());
        Console.WriteLine(string.Join(",",nameDisList));
        Console.WriteLine(string.Join(",",ageDisList));
    }
}

class PersonNameEqualityComparer :IEqualityComparer<Person>
{
    public bool Equals(Person b1, Person b2)
    {
        if (ReferenceEquals(b1, b2))
            return true;
        if (b2 is null || b1 is null)
            return false;
        return b1.Name == b2.Name;
    }
    public int GetHashCode(Person p) => p.NameGetHashCode();
}

class PersonAgeEqualityComparer :IEqualityComparer<Person>
{
    public bool Equals(Person b1, Person b2)
    {
        if (ReferenceEquals(b1, b2))
            return true;
        if (b2 is null || b1 is null)
            return false;
        return b1.Age == b2.Age;
    }
    public int GetHashCode(Person p) => p.AgeGetHashCode();
}

IEquatable<T>

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

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

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

public class Person : IEquatable<Person>
{
    public string SSN
    {
        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.SSN == other.SSN)
            return true;
        else
            return false;
    }

    public override int GetHashCode()
    {
        return this.SSN.GetHashCode();
    }

    public static bool operator ==(Person person1, Person person2)
    {
        if (((object)person1) == null || ((object)person2) == null)
            return Equals(person1, person2);

        return person1.Equals(person2);
    }

    public static bool operator !=(Person person1, Person person2)
    {
        return !(person1 == person2); // 使用不等于即可
    }
}

后言

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

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